VvebOIDC/system/routes.php

273 lines
6.8 KiB
PHP

<?php
/**
* Vvveb
*
* Copyright (C) 2022 Ziadin Givan
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Vvveb\System;
class Routes {
const stringRegex = '{(\w+)}';
const varRegex = '[{#]([a-zA-Z]\w+)({([\d,]+)})?[#}]';
const stringLimitRegex = '{([a-zA-Z]\w+){([\d,]+)}}';
const numericRegex = '#([a-zA-Z]\w+)#';
const numericLimitRegex = '#([a-zA-Z]\w+){([\d,]+)}#';
const wildcardRegex = '\*';
private static $routes = [];
private static $urls = [];
private static $modules = null;
private static $init = false;
private static function processRoute($url, $data) {
$module = $data['module'];
//self :: $modules[$module] = $url;
$parameters = [];
if (preg_match_all('/' . self :: varRegex . '/', $url, $matches)) {
if ($matches[1]) {
$parameters = $matches[1];
}
}
self :: $modules[$module][] = ['url' => $url, 'parameters' => $parameters, 'count' => count($parameters)];
//add urls with most parameters first for proper matching
uasort(self :: $modules[$module], function ($a, $b) {
return $b['count'] <=> $a['count'];
});
$route = $url;
//escape / for regex
$url = str_replace('/', '\/', $url);
//numeric limit
$url = preg_replace('/' . self :: numericLimitRegex . '/', '(?<$1>\d{$2})', $url);
//numeric
$url = preg_replace('/' . self :: numericRegex . '/', '(?<$1>\d+)', $url);
//string limit
$url = preg_replace('/' . self :: stringLimitRegex . '/', '(?<$1>[^$\/]{$2})', $url);
//string
$url = preg_replace('/' . self :: stringRegex . '/', '(?<$1>[^$\/]+)', $url);
//wildcard
$url = preg_replace('/' . self :: wildcardRegex . '/', '.*?', $url);
self :: $urls[$route] = [$url, $module];
return $url;
}
public static function addRoute($url, $data) {
self :: $routes[$url] = $data;
self :: processRoute($url, $data);
}
public static function removeRoute($url) {
if (is_array($url)) {
foreach ($url as $route) {
$module = self :: $urls[$route] ?? false;
if ($module) {
unset(self :: $modules[$module]);
}
unset(self :: $urls[$route]);
}
} else {
$module = self :: $urls[$url] ?? false;
if ($module) {
unset(self :: $modules[$module]);
}
unset(self :: $urls[$url]);
}
}
public static function init($app = 'app') {
$cacheDriver = Cache :: getInstance();
$cacheKey = $app;
if ($result = $cacheDriver->get('routes', $cacheKey)) {
static::$routes = $result['routes'];
static::$urls = $result['urls'];
static::$modules = $result['modules'];
} else {
$routesConfig = DIR_ROOT . "/config/$app-routes.php";
if (file_exists($routesConfig)) {
self :: $routes += include $routesConfig;
}
list(self :: $routes) = Event::trigger(__CLASS__, __FUNCTION__ , self :: $routes);
foreach (self :: $routes as $url => $data) {
self :: processRoute($url, $data);
}
$result = [];
$result['routes'] = static::$routes;
$result['modules'] = static::$modules;
$result['urls'] = static::$urls;
$cacheDriver->set('routes', $cacheKey, $result);
}
self :: $init = true;
return true;
}
public static function match($url) {
if (! self :: $init) {
self :: init();
}
//remove get parameters
$url = preg_replace('/\?.*$/', '', str_replace('//', '/', $url));
foreach (self :: $urls as $route => $data) {
$params = static::$routes[$route] ?? '';
list($pattern, $module) = $data;
if ($url == $pattern || preg_match('/^' . $pattern . '$/', $url, $matches)) {
$parameters = array_filter($matches, 'is_string', ARRAY_FILTER_USE_KEY);
$parameters['route'] = $route;
$parameters['module'] = $module;
$parameters['pattern'] = $pattern;
$parameters += $params;
return $parameters;
}
}
return false;
}
public static function get($route) {
return self :: $routes[$route] ?? [];
}
public static function getRoutes() {
return self :: $routes ?? [];
}
public static function varReplace($url, $parameters) {
return preg_replace_callback('/' . self :: stringRegex . '|' . self :: numericRegex . '/',
function ($matches) use ($parameters) {
$var = $matches[1];
if (isset($parameters[$var])) {
return $parameters[$var];
}
return '';
}, $url);
}
public static function getRouteData($module) {
if (! self :: $init) {
self :: init();
}
return self :: $modules[$module] ?? [];
}
public static function getUrlData($url = false) {
if (! $url) {
$url = \Vvveb\getCurrentUrl();
}
$parameters = self :: match($url);
$route = stripslashes($parameters['route'] ?? '');
if ($parameters) {
//$parameters['pattern'] = self :: $modules[$parameters['route']][0]['url'];
$parameters = $parameters + self :: $routes[$route];
if (isset($parameters['edit'])) {
$parameters['edit'] = self :: varReplace($parameters['edit'], $parameters);
}
}
return $parameters;
}
public static function url($route, $parameters = false) {
if (! self :: $init) {
self :: init();
}
if (isset(self :: $modules[$route])) {
$pattern = self :: $modules[$route][0]['url'] ?? '';
if ($parameters && isset($parameters['route'])) {
unset($parameters['route']);
}
$parameters_count = is_array($parameters) ? count($parameters) : 0;
$param_keys = is_array($parameters) ? array_keys($parameters) : [];
//if ($param_keys) {
foreach (self :: $modules[$route] as $value) {
//select route that has all parameters
if ($value['parameters'] && $parameters_count) {
if (count(array_intersect($value['parameters'], $param_keys)) === count($value['parameters'])) {
$pattern = $value['url'];
break;
}
} else {
$no_parameters = $value['url'];
}
}
//}
if (! $parameters) {
$pattern = $no_parameters;
}
$missing = false;
$url = preg_replace_callback('/' . self :: varRegex . '/',
function ($matches) use ($parameters, &$missing) {
$var = $matches[1];
if (isset($parameters[$var])) {
return $parameters[$var];
} else {
$missing = true;
}
return '';
}, $pattern);
if ($missing) {
return '/?route=' . $route . '&' . (is_array($parameters) ? http_build_query($parameters) : '');
} else {
return $url;
}
}
}
}