'application/Espo/Resources/routes.json', 'modulePath' => 'application/Espo/Modules/{*}/Resources/routes.json', 'customPath' => 'custom/Espo/Custom/Resources/routes.json', ]; private $config; private $metadata; private $fileManager; private $dataCache; public function __construct( Config $config, Metadata $metadata, FileManager $fileManager, DataCache $dataCache ) { $this->config = $config; $this->metadata = $metadata; $this->fileManager = $fileManager; $this->dataCache = $dataCache; } /** * Get all routes. */ public function getFullList(): array { if (!isset($this->data)) { $this->init(); } return $this->data; } private function init(): void { $useCache = $this->config->get('useCache'); if ($this->dataCache->has($this->cacheKey) && $useCache) { $this->data = $this->dataCache->get($this->cacheKey); return; } $this->data = $this->unify(); if ($useCache) { $this->dataCache->store($this->cacheKey, $this->data); } } private function unify(): array { $data = $this->addDataFromFile([], $this->paths['customPath']); $moduleData = []; foreach ($this->metadata->getModuleList() as $moduleName) { $modulePath = str_replace('{*}', $moduleName, $this->paths['modulePath']); foreach ($this->addDataFromFile([], $modulePath) as $row) { $key = $row['method'] . $row['route']; $moduleData[$key] = $row; } } $data = array_merge($data, array_values($moduleData)); $data = $this->addDataFromFile($data, $this->paths['corePath']); return $data; } private function addDataFromFile(array $currentData, string $routeFile): array { if (!file_exists($routeFile)) { return $currentData; } $content = $this->fileManager->getContents($routeFile); $data = Json::decode($content, true); return $this->appendRoutesToData($currentData, $data); } private function appendRoutesToData(array $data, array $newData): array { foreach ($newData as $route) { $route['route'] = $this->adjustPath($route['route']); if (isset($route['conditions'])) { $route['noAuth'] = !($route['conditions']['auth'] ?? true); unset($route['conditions']); } if (self::isRouteInList($route, $data)) { continue; } $data[] = $route; } return $data; } /** * Check and adjust the route path. */ private function adjustPath(string $routePath): string { $routePath = trim($routePath); // to fast route format $routePath = preg_replace('/\:([a-zA-Z0-9]+)/', '{${1}}', $routePath); if (substr($routePath, 0, 1) !== '/') { return '/' . $routePath; } return $routePath; } public static function detectBasePath(): string { $scriptName = parse_url($_SERVER['SCRIPT_NAME'] , PHP_URL_PATH); $scriptDir = dirname($scriptName); $uri = parse_url('http://any.com' . $_SERVER['REQUEST_URI'], PHP_URL_PATH); if (stripos($uri, $scriptName) === 0) { return $scriptName; } if ($scriptDir !== '/' && stripos($uri, $scriptDir) === 0) { return $scriptDir; } return ''; } public static function detectEntryPointRoute(): string { $basePath = self::detectBasePath(); $uri = parse_url('http://any.com' . $_SERVER['REQUEST_URI'], PHP_URL_PATH); if ($uri === $basePath) { return '/'; } if (stripos($uri, $basePath) === 0) { return substr($uri, strlen($basePath)); } return '/'; } static private function isRouteInList(array $newRoute, array $routeList): bool { foreach ($routeList as $route) { if (Util::areEqual($route, $newRoute)) { return true; } } return false; } }