Passed
Push — master ( 0ee5cf...8209bc )
by Fran
02:47
created

Router::getRoutes()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 2
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
namespace PSFS\base;
3
4
use Exception;
5
use InvalidArgumentException;
6
use PSFS\base\config\Config;
7
use PSFS\base\dto\JsonResponse;
8
use PSFS\base\exception\AccessDeniedException;
9
use PSFS\base\exception\AdminCredentialsException;
10
use PSFS\base\exception\ConfigException;
11
use PSFS\base\exception\RouterException;
12
use PSFS\base\types\Controller;
13
use PSFS\base\types\helpers\AnnotationHelper;
14
use PSFS\base\types\helpers\GeneratorHelper;
15
use PSFS\base\types\helpers\I18nHelper;
16
use PSFS\base\types\helpers\Inspector;
17
use PSFS\base\types\helpers\RouterHelper;
18
use PSFS\base\types\helpers\SecurityHelper;
19
use PSFS\base\types\traits\SingletonTrait;
20
use PSFS\controller\base\Admin;
21
use PSFS\services\AdminServices;
22
use ReflectionClass;
23
use ReflectionException;
24
use ReflectionMethod;
25
use Symfony\Component\Finder\Finder;
26
use Symfony\Component\Finder\SplFileInfo;
27
28
/**
29
 * Class Router
30
 * @package PSFS
31
 */
32
class Router
33
{
34
    use SingletonTrait;
35
36
    /**
37
     * @var array
38
     */
39
    private $routing = [];
40
    /**
41
     * @var array
42
     */
43
    private $slugs = [];
44
    /**
45
     * @var array
46
     */
47
    private $domains = [];
48
    /**
49
     * @var Finder $finder
50
     */
51
    private $finder;
52
    /**
53
     * @var Cache $cache
54
     */
55
    private $cache;
56
    /**
57
     * @var bool headersSent
58
     */
59
    protected $headersSent = false;
60
    /**
61
     * @var int
62
     */
63
    protected $cacheType = Cache::JSON;
64
65
    /**
66
     * Router constructor.
67
     * @throws exception\GeneratorException
68
     * @throws ConfigException
69
     * @throws InvalidArgumentException
70
     * @throws ReflectionException
71
     */
72 1
    public function __construct()
73
    {
74 1
        $this->finder = new Finder();
75 1
        $this->cache = Cache::getInstance();
76 1
        $this->init();
77 1
    }
78
79
    /**
80
     * @throws exception\GeneratorException
81
     * @throws ConfigException
82
     * @throws InvalidArgumentException
83
     * @throws ReflectionException
84
     */
85 1
    public function init()
86
    {
87 1
        list($this->routing, $this->slugs) = $this->cache->getDataFromFile(CONFIG_DIR . DIRECTORY_SEPARATOR . 'urls.json', $this->cacheType, TRUE);
88 1
        if (empty($this->routing) || Config::getParam('debug', true)) {
89 1
            $this->debugLoad();
90
        } else {
91
            $this->domains = $this->cache->getDataFromFile(CONFIG_DIR . DIRECTORY_SEPARATOR . 'domains.json', $this->cacheType, TRUE);
92
        }
93 1
        $this->checkExternalModules(false);
94 1
        $this->setLoaded();
95 1
    }
96
97
    /**
98
     * @throws exception\GeneratorException
99
     * @throws ConfigException
100
     * @throws InvalidArgumentException
101
     * @throws ReflectionException
102
     */
103 1
    private function debugLoad() {
104 1
        Logger::log('Begin routes load');
105 1
        $this->hydrateRouting();
106 1
        $this->simpatize();
107 1
        Logger::log('End routes load');
108 1
    }
109
110
    /**
111
     * @param Exception|NULL $exception
112
     * @param bool $isJson
113
     * @return string
114
     * @throws RouterException
115
     * @throws exception\GeneratorException
116
     */
117
    public function httpNotFound(Exception $exception = NULL, $isJson = false)
118
    {
119
        Inspector::stats('[Router] Throw not found exception', Inspector::SCOPE_DEBUG);
120
        if (NULL === $exception) {
121
            Logger::log('Not found page thrown without previous exception', LOG_WARNING);
122
            $exception = new Exception(t('Page not found'), 404);
123
        }
124
        $template = Template::getInstance()->setStatus($exception->getCode());
125
        if ($isJson || false !== stripos(Request::getInstance()->getServer('CONTENT_TYPE'), 'json')) {
126
            $response = new JsonResponse(null, false, 0, 0, $exception->getMessage());
127
            return $template->output(json_encode($response), 'application/json');
128
        }
129
130
        $notFoundRoute = Config::getParam('route.404');
131
        if(null !== $notFoundRoute) {
132
            Request::getInstance()->redirect($this->getRoute($notFoundRoute, true));
133
        } else {
134
            return $template->render('error.html.twig', array(
135
                'exception' => $exception,
136
                'trace' => $exception->getTraceAsString(),
137
                'error_page' => TRUE,
138
            ));
139
        }
140
    }
141
142
    /**
143
     * @return array
144
     */
145 1
    public function getSlugs()
146
    {
147 1
        return $this->slugs;
148
    }
149
150
    /**
151
     * @return array
152
     */
153 2
    public function getRoutes() {
154 2
        return $this->routing;
155
    }
156
157
    /**
158
     * Method that extract all routes in the platform
159
     * @return array
160
     */
161 1
    public function getAllRoutes()
162
    {
163 1
        $routes = [];
164 1
        foreach ($this->getRoutes() as $path => $route) {
165 1
            if (array_key_exists('slug', $route)) {
166 1
                $routes[$route['slug']] = $path;
167
            }
168
        }
169 1
        return $routes;
170
    }
171
172
    /**
173
     * @param string|null $route
174
     *
175
     * @throws Exception
176
     * @return string HTML
177
     */
178 2
    public function execute($route)
179
    {
180 2
        Inspector::stats('[Router] Executing the request', Inspector::SCOPE_DEBUG);
181
        try {
182
            //Search action and execute
183 2
            return $this->searchAction($route);
184 1
        } catch (AccessDeniedException $e) {
185
            Logger::log(t('Solicitamos credenciales de acceso a zona restringida'), LOG_WARNING, ['file' => $e->getFile() . '[' . $e->getLine() . ']']);
186
            return Admin::staticAdminLogon($route);
187 1
        } catch (RouterException $r) {
188 1
            Logger::log($r->getMessage(), LOG_WARNING);
189
        } catch (Exception $e) {
190
            Logger::log($e->getMessage(), LOG_ERR);
191
            throw $e;
192
        }
193
194 1
        throw new RouterException(t('Página no encontrada'), 404);
195
    }
196
197
    /**
198
     * @param string $route
199
     * @return mixed
200
     * @throws AccessDeniedException
201
     * @throws AdminCredentialsException
202
     * @throws RouterException
203
     * @throws Exception
204
     */
205 2
    protected function searchAction($route)
206
    {
207 2
        Inspector::stats('[Router] Searching action to execute: ' . $route, Inspector::SCOPE_DEBUG);
208
        //Revisamos si tenemos la ruta registrada
209 2
        $parts = parse_url($route);
210 2
        $path = array_key_exists('path', $parts) ? $parts['path'] : $route;
211 2
        $httpRequest = Request::getInstance()->getMethod();
212 2
        foreach ($this->routing as $pattern => $action) {
213 2
            list($httpMethod, $routePattern) = RouterHelper::extractHttpRoute($pattern);
214 2
            $matched = RouterHelper::matchRoutePattern($routePattern, $path);
215 2
            if ($matched && ($httpMethod === 'ALL' || $httpRequest === $httpMethod) && RouterHelper::compareSlashes($routePattern, $path)) {
216
                // Checks restricted access
217 1
                SecurityHelper::checkRestrictedAccess($route);
218 1
                $get = RouterHelper::extractComponents($route, $routePattern);
219
                /** @var $class Controller */
220 1
                $class = RouterHelper::getClassToCall($action);
221
                try {
222 1
                    if($this->checkRequirements($action, $get)) {
223 1
                        return $this->executeCachedRoute($route, $action, $class, $get);
0 ignored issues
show
Bug introduced by
$class of type PSFS\base\types\Controller is incompatible with the type string expected by parameter $class of PSFS\base\Router::executeCachedRoute(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

223
                        return $this->executeCachedRoute($route, $action, /** @scrutinizer ignore-type */ $class, $get);
Loading history...
224
                    } else {
225
                        throw new RouterException(t('La ruta no es válida'), 400);
226
                    }
227
                } catch (Exception $e) {
228
                    Logger::log($e->getMessage(), LOG_ERR);
229 2
                    throw $e;
230
                }
231
            }
232
        }
233 1
        throw new RouterException(t('Ruta no encontrada'));
234
    }
235
236
    /**
237
     * @param array $action
238
     * @param array $params
239
     * @return bool
240
     */
241 1
    private function checkRequirements(array $action, $params = []) {
242 1
        Inspector::stats('[Router] Checking request requirements', Inspector::SCOPE_DEBUG);
243 1
        if(!empty($params) && !empty($action['requirements'])) {
244
            $checked = 0;
245
            foreach(array_keys($params) as $key) {
246
                if(in_array($key, $action['requirements'], true)) {
247
                    $checked++;
248
                }
249
            }
250
            $valid = count($action['requirements']) === $checked;
251
        } else {
252 1
            $valid = true;
253
        }
254 1
        return $valid;
255
    }
256
257
    /**
258
     * @return string HTML
259
     */
260
    protected function sentAuthHeader()
261
    {
262
        return AdminServices::getInstance()->setAdminHeaders();
263
    }
264
265
    /**
266
     * @return string|null
267
     */
268 1
    private function getExternalModules() {
269 1
        $externalModules = Config::getParam('modules.extend', '');
270 1
        $externalModules .= ',psfs/auth,psfs/nosql';
271 1
        return $externalModules;
272
    }
273
274
    /**
275
     * @param boolean $hydrateRoute
276
     */
277 1
    private function checkExternalModules($hydrateRoute = true)
278
    {
279 1
        $externalModules = $this->getExternalModules();
280 1
        if ('' !== $externalModules) {
281 1
            $externalModules = explode(',', $externalModules);
282 1
            foreach ($externalModules as &$module) {
283 1
                $module = $this->loadExternalModule($hydrateRoute, $module);
284
            }
285
        }
286 1
    }
287
288
    /**
289
     * @throws ConfigException
290
     * @throws InvalidArgumentException
291
     * @throws ReflectionException
292
     * @throws exception\GeneratorException
293
     */
294 1
    private function generateRouting()
295
    {
296 1
        $base = SOURCE_DIR;
297 1
        $modulesPath = realpath(CORE_DIR);
298 1
        $this->routing = $this->inspectDir($base, 'PSFS', array());
299 1
        $this->checkExternalModules();
300 1
        if (file_exists($modulesPath)) {
301
            $modules = $this->finder->directories()->in($modulesPath)->depth(0);
302
            if($modules->hasResults()) {
303
                foreach ($modules->getIterator() as $modulePath) {
304
                    $module = $modulePath->getBasename();
305
                    $this->routing = $this->inspectDir($modulesPath . DIRECTORY_SEPARATOR . $module, $module, $this->routing);
306
                }
307
            }
308
        }
309 1
        $this->cache->storeData(CONFIG_DIR . DIRECTORY_SEPARATOR . 'domains.json', $this->domains, Cache::JSON, TRUE);
310 1
    }
311
312
    /**
313
     * @throws exception\GeneratorException
314
     * @throws ConfigException
315
     * @throws InvalidArgumentException
316
     * @throws ReflectionException
317
     */
318 1
    public function hydrateRouting()
319
    {
320 1
        $this->generateRouting();
321 1
        $home = Config::getParam('home.action', 'admin');
322 1
        if (NULL !== $home || $home !== '') {
323 1
            $homeParams = NULL;
324 1
            foreach ($this->routing as $pattern => $params) {
325 1
                list($method, $route) = RouterHelper::extractHttpRoute($pattern);
326 1
                if (preg_match('/' . preg_quote($route, '/') . '$/i', '/' . $home)) {
327 1
                    $homeParams = $params;
328
                }
329 1
                unset($method);
330
            }
331 1
            if (NULL !== $homeParams) {
332 1
                $this->routing['/'] = $homeParams;
333
            }
334
        }
335 1
    }
336
337
    /**
338
     * @param string $origen
339
     * @param string $namespace
340
     * @param array $routing
341
     * @return array
342
     * @throws ReflectionException
343
     * @throws ConfigException
344
     * @throws InvalidArgumentException
345
     */
346 1
    private function inspectDir($origen, $namespace = 'PSFS', $routing = [])
347
    {
348 1
        $files = $this->finder->files()->in($origen)->path('/(controller|api)/i')->depth('< 3')->name('*.php');
349 1
        if($files->hasResults()) {
350 1
            foreach ($files->getIterator() as $file) {
351 1
                if($namespace !== 'PSFS' && method_exists($file, 'getRelativePathname')) {
352
                    $filename = '\\' . str_replace('/', '\\', str_replace($origen, '', $file->getRelativePathname()));
353
                } else {
354 1
                    $filename = str_replace('/', '\\', str_replace($origen, '', $file->getPathname()));
355
                }
356 1
                $routing = $this->addRouting($namespace . str_replace('.php', '', $filename), $routing, $namespace);
357
            }
358
        }
359 1
        $this->finder = new Finder();
360
361 1
        return $routing;
362
    }
363
364
    /**
365
     * @param string $namespace
366
     * @return bool
367
     */
368 3
    public static function exists($namespace)
369
    {
370 3
        return (class_exists($namespace) || interface_exists($namespace) || trait_exists($namespace));
371
    }
372
373
    /**
374
     * @param string $namespace
375
     * @param array $routing
376
     * @param string $module
377
     * @return array
378
     * @throws ReflectionException
379
     */
380 1
    private function addRouting($namespace, &$routing, $module = 'PSFS')
381
    {
382 1
        if (self::exists($namespace)) {
383 1
            if(I18nHelper::checkI18Class($namespace)) {
384
                return $routing;
385
            }
386 1
            $reflection = new ReflectionClass($namespace);
387 1
            if (false === $reflection->isAbstract() && FALSE === $reflection->isInterface()) {
388 1
                $this->extractDomain($reflection);
389 1
                $classComments = $reflection->getDocComment();
390 1
                $api = AnnotationHelper::extractApi($classComments);
391 1
                foreach ($reflection->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
392 1
                    $route = AnnotationHelper::extractRoute($method->getDocComment());
393 1
                    if (null !== $route) {
394 1
                        list($route, $info) = RouterHelper::extractRouteInfo($method, str_replace('\\', '', $api), str_replace('\\', '', $module));
395
396 1
                        if (null !== $route && null !== $info) {
397 1
                            $info['class'] = $namespace;
398 1
                            $routing[$route] = $info;
399
                        }
400
                    }
401
                }
402
            }
403
        }
404
405 1
        return $routing;
406
    }
407
408
    /**
409
     *
410
     * @param ReflectionClass $class
411
     *
412
     * @return Router
413
     * @throws ConfigException
414
     */
415 1
    protected function extractDomain(ReflectionClass $class)
416
    {
417
        //Calculamos los dominios para las plantillas
418 1
        if ($class->hasConstant('DOMAIN') && !$class->isAbstract()) {
419 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...
420 1
                $this->domains = [];
421
            }
422 1
            $domain = '@' . $class->getConstant('DOMAIN') . '/';
423 1
            if (!array_key_exists($domain, $this->domains)) {
424 1
                $this->domains[$domain] = RouterHelper::extractDomainInfo($class, $domain);
425
            }
426
        }
427
428 1
        return $this;
429
    }
430
431
    /**
432
     * @return $this
433
     * @throws exception\GeneratorException
434
     * @throws ConfigException
435
     */
436 1
    public function simpatize()
437
    {
438 1
        $translationFileName = 'translations' . DIRECTORY_SEPARATOR . 'routes_translations.php';
439 1
        $absoluteTranslationFileName = CACHE_DIR . DIRECTORY_SEPARATOR . $translationFileName;
440 1
        $this->generateSlugs($absoluteTranslationFileName);
441 1
        GeneratorHelper::createDir(CONFIG_DIR);
442 1
        Cache::getInstance()->storeData(CONFIG_DIR . DIRECTORY_SEPARATOR . 'urls.json', array($this->routing, $this->slugs), Cache::JSON, TRUE);
443
444 1
        return $this;
445
    }
446
447
    /**
448
     * @param string $slug
449
     * @param boolean $absolute
450
     * @param array $params
451
     *
452
     * @return string|null
453
     * @throws RouterException
454
     */
455 3
    public function getRoute($slug = '', $absolute = FALSE, array $params = [])
456
    {
457 3
        if ('' === $slug) {
458 1
            return $absolute ? Request::getInstance()->getRootUrl() . '/' : '/';
459
        }
460 3
        if (!is_array($this->slugs) || !array_key_exists($slug, $this->slugs)) {
0 ignored issues
show
introduced by
The condition is_array($this->slugs) is always true.
Loading history...
461 1
            throw new RouterException(t('No existe la ruta especificada'));
462
        }
463 3
        $url = $absolute ? Request::getInstance()->getRootUrl() . $this->slugs[$slug] : $this->slugs[$slug];
464 3
        if (!empty($params)) {
465
            foreach ($params as $key => $value) {
466
                $url = str_replace('{' . $key . '}', $value, $url);
467
            }
468 3
        } elseif (!empty($this->routing[$this->slugs[$slug]]['default'])) {
469 3
            $url = $absolute ? Request::getInstance()->getRootUrl() . $this->routing[$this->slugs[$slug]]['default'] : $this->routing[$this->slugs[$slug]]['default'];
470
        }
471
472 3
        return preg_replace('/(GET|POST|PUT|DELETE|ALL)\#\|\#/', '', $url);
473
    }
474
475
    /**
476
     * @return array
477
     */
478 2
    public function getDomains()
479
    {
480 2
        return $this->domains ?: [];
481
    }
482
483
    /**
484
     * @param string $class
485
     * @param string $method
486
     */
487 1
    private function checkPreActions($class, $method) {
488 1
        $preAction = 'pre' . ucfirst($method);
489 1
        if(method_exists($class, $preAction)) {
490
            Inspector::stats('[Router] Pre action invoked', Inspector::SCOPE_DEBUG);
491
            try {
492
                if(false === call_user_func_array([$class, $preAction])) {
0 ignored issues
show
Bug introduced by
The call to call_user_func_array() has too few arguments starting with param_arr. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

492
                if(false === /** @scrutinizer ignore-call */ call_user_func_array([$class, $preAction])) {

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
493
                    Logger::log(t('Pre action failed'), LOG_ERR, [error_get_last()]);
494
                    error_clear_last();
495
                }
496
            } catch (Exception $e) {
497
                Logger::log($e->getMessage(), LOG_ERR, [$class, $method]);
498
            }
499
        }
500 1
    }
501
502
    /**
503
     * @param string $route
504
     * @param array $action
505
     * @param string $class
506
     * @param array $params
507
     * @return mixed
508
     * @throws exception\GeneratorException
509
     * @throws ConfigException
510
     */
511 1
    protected function executeCachedRoute($route, $action, $class, $params = NULL)
512
    {
513 1
        Inspector::stats('[Router] Executing route ' . $route, Inspector::SCOPE_DEBUG);
514 1
        $action['params'] = array_merge($action['params'], $params, Request::getInstance()->getQueryParams());
515 1
        Security::getInstance()->setSessionKey(Cache::CACHE_SESSION_VAR, $action);
516 1
        $cache = Cache::needCache();
517 1
        $execute = TRUE;
518 1
        $return = null;
519 1
        if (FALSE !== $cache && $action['http'] === 'GET' && Config::getParam('debug') === FALSE) {
520
            list($path, $cacheDataName) = $this->cache->getRequestCacheHash();
521
            $cachedData = $this->cache->readFromCache('json' . DIRECTORY_SEPARATOR . $path . $cacheDataName, $cache);
0 ignored issues
show
Bug introduced by
It seems like $cache can also be of type true; however, parameter $expires of PSFS\base\Cache::readFromCache() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

521
            $cachedData = $this->cache->readFromCache('json' . DIRECTORY_SEPARATOR . $path . $cacheDataName, /** @scrutinizer ignore-type */ $cache);
Loading history...
522
            if (NULL !== $cachedData) {
523
                $headers = $this->cache->readFromCache('json' . DIRECTORY_SEPARATOR . $path . $cacheDataName . '.headers', $cache, null, Cache::JSON);
524
                Template::getInstance()->renderCache($cachedData, $headers);
525
                $execute = FALSE;
526
            }
527
        }
528 1
        if ($execute) {
529 1
            Inspector::stats('[Router] Start executing action ' . $route, Inspector::SCOPE_DEBUG);
530 1
            $this->checkPreActions($class, $action['method']);
531 1
            $return = call_user_func_array([$class, $action['method']], $params);
0 ignored issues
show
Bug introduced by
It seems like $params can also be of type null; however, parameter $param_arr of call_user_func_array() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

531
            $return = call_user_func_array([$class, $action['method']], /** @scrutinizer ignore-type */ $params);
Loading history...
532 1
            if (false === $return) {
533
                Logger::log(t('An error occurred trying to execute the action'), LOG_ERR, [error_get_last()]);
534
            }
535
        }
536 1
        return $return;
537
    }
538
539
    /**
540
     * Parse slugs to create translations
541
     *
542
     * @param string $absoluteTranslationFileName
543
     */
544 1
    private function generateSlugs($absoluteTranslationFileName)
545
    {
546 1
        $translations = I18nHelper::generateTranslationsFile($absoluteTranslationFileName);
547 1
        foreach ($this->routing as $key => &$info) {
548 1
            $keyParts = explode('#|#', $key);
549 1
            $keyParts = array_key_exists(1, $keyParts) ? $keyParts[1] : $keyParts[0];
550 1
            $slug = RouterHelper::slugify($keyParts);
551 1
            if (NULL !== $slug && !array_key_exists($slug, $translations)) {
552 1
                $translations[$slug] = $info['label'];
553 1
                file_put_contents($absoluteTranslationFileName, "\$translations[\"{$slug}\"] = t(\"{$info['label']}\");\n", FILE_APPEND);
554
            }
555 1
            $this->slugs[$slug] = $key;
556 1
            $info['slug'] = $slug;
557
        }
558 1
    }
559
560
    /**
561
     * @param bool $hydrateRoute
562
     * @param $modulePath
563
     * @param $externalModulePath
564
     */
565
    private function loadExternalAutoloader($hydrateRoute, SplFileInfo $modulePath, $externalModulePath)
566
    {
567
        $extModule = $modulePath->getBasename();
568
        $moduleAutoloader = realpath($externalModulePath . DIRECTORY_SEPARATOR . $extModule . DIRECTORY_SEPARATOR . 'autoload.php');
569
        if(file_exists($moduleAutoloader)) {
570
            include_once $moduleAutoloader;
571
            if ($hydrateRoute) {
572
                $this->routing = $this->inspectDir($externalModulePath . DIRECTORY_SEPARATOR . $extModule, '\\' . $extModule, $this->routing);
573
            }
574
        }
575
    }
576
577
    /**
578
     * @param $hydrateRoute
579
     * @param $module
580
     * @return mixed
581
     */
582 1
    private function loadExternalModule($hydrateRoute, $module)
583
    {
584
        try {
585 1
            $module = preg_replace('/(\\\|\/)/', DIRECTORY_SEPARATOR, $module);
586 1
            $externalModulePath = VENDOR_DIR . DIRECTORY_SEPARATOR . $module . DIRECTORY_SEPARATOR . 'src';
587 1
            if(file_exists($externalModulePath)) {
588
                $externalModule = $this->finder->directories()->in($externalModulePath)->depth(0);
589
                if($externalModule->hasResults()) {
590
                    foreach ($externalModule->getIterator() as $modulePath) {
591 1
                        $this->loadExternalAutoloader($hydrateRoute, $modulePath, $externalModulePath);
592
                    }
593
                }
594
            }
595
        } catch (Exception $e) {
596
            Logger::log($e->getMessage(), LOG_WARNING);
597
            $module = null;
598
        }
599 1
        return $module;
600
    }
601
602
}
603