Exception:
Array
(
    [message] => syntax error, unexpected identifier "stringRegex", expecting "="
    [file] => /home/johannes/web/transferworld.nl/public_html/system/routes.php
    [line_no] => 25
    [line] => 	const string stringRegex = '{(\w+)}';	 // <==

    [lines] => Array
        (
            [0] =>  * along with this program.  If not, see .

            [1] =>  *

            [2] =>  */

            [3] => 

            [4] => namespace Vvveb\System;

            [5] => 

            [6] => class Routes {

            [7] => 	const string stringRegex = '{(\w+)}';	 // <==

            [8] => 

            [9] => 	const string varRegex = '[{#]([a-zA-Z]\w+)({([\d,]+)})?[#}]';

            [10] => 

            [11] => 	const string stringLimitRegex = '{([a-zA-Z]\w+){([\d,]+)}}';

            [12] => 

            [13] => 	const string numericRegex = '#([a-zA-Z]\w+)#';

        )

    [trace] => #0 /home/johannes/web/transferworld.nl/public_html/system/core/frontcontroller.php(387): Vvveb\System\Core\autoload()
#1 /home/johannes/web/transferworld.nl/public_html/system/core/startup.php(514): Vvveb\System\Core\FrontController::dispatch()
#2 /home/johannes/web/transferworld.nl/public_html/index.php(120): Vvveb\System\Core\start()
#3 /home/johannes/web/transferworld.nl/public_html/index.php(166): Vvveb\saveCache()
#4 {main}
    [code] => Array
        (
            [0] =>  

            [2] => /**

            [3] =>  * Vvveb

            [4] =>  *

            [5] =>  * Copyright (C) 2022  Ziadin Givan

            [6] =>  *

            [7] =>  * This program is free software: you can redistribute it and/or modify

            [8] =>  * it under the terms of the GNU Affero General Public License as

            [9] =>  * published by the Free Software Foundation, either version 3 of the

            [10] =>  * License, or (at your option) any later version.

            [11] =>  *

            [12] =>  * This program is distributed in the hope that it will be useful,

            [13] =>  * but WITHOUT ANY WARRANTY; without even the implied warranty of

            [14] =>  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

            [15] =>  * GNU Affero General Public License for more details.

            [16] =>  *

            [17] =>  * You should have received a copy of the GNU Affero General Public License

            [18] =>  * along with this program.  If not, see .

            [19] =>  *

            [20] =>  */

            [21] => 

            [22] => namespace Vvveb\System;

            [23] => 

            [24] => class Routes {

            [25] => 	const string stringRegex = '{(\w+)}';	 // <==

            [26] => 

            [27] => 	const string varRegex = '[{#]([a-zA-Z]\w+)({([\d,]+)})?[#}]';

            [28] => 

            [29] => 	const string stringLimitRegex = '{([a-zA-Z]\w+){([\d,]+)}}';

            [30] => 

            [31] => 	const string numericRegex = '#([a-zA-Z]\w+)#';

            [32] => 

            [33] => 	const string numericLimitRegex = '#([a-zA-Z]\w+){([\d,]+)}#';

            [34] => 

            [35] => 	const string wildcardRegex = '\*';

            [36] => 

            [37] => 	/** @var array */

            [38] => 	private static array $routes = [];

            [39] => 

            [40] => 	/** @var array */

            [41] => 	private static array $urls = [];

            [42] => 

            [43] => 	/** @var array, count: int}> >|null */

            [44] => 	private static ?array $modules = null;

            [45] => 

            [46] => 	private static bool $init = false;

            [47] => 

            [48] => 	/**

            [49] => 	 * Process a route pattern into regex and store module data.

            [50] => 	 *

            [51] => 	 * @param string $url  The route URL pattern.

            [52] => 	 * @param array{module: string, edit?: string} $data  Route configuration data.

            [53] => 	 * @return string The compiled regex pattern.

            [54] => 	 */

            [55] => 	private static function processRoute(string $url, array $data): string {

            [56] => 		$module = $data['module'];

            [57] => 		unset($data['edit'], $data['module']);

            [58] => 

            [59] => 		$parameters = [];

            [60] => 

            [61] => 		if (preg_match_all('/' . self :: varRegex . '/', $url, $matches)) {

            [62] => 			if ($matches[1]) {

            [63] => 				$parameters = $matches[1];

            [64] => 			}

            [65] => 		}

            [66] => 

            [67] => 		if ($data) {

            [68] => 			$parameters = array_merge($parameters, array_keys($data));

            [69] => 		}

            [70] => 

            [71] => 		self :: $modules[$module][] = ['url' => $url, 'parameters' => $parameters, 'count' => count($parameters)];

            [72] => 		uasort(self :: $modules[$module], function (array $a, array $b): int {

            [73] => 			return $b['count'] <=> $a['count'];

            [74] => 		});

            [75] => 

            [76] => 		$route = $url;

            [77] => 		$url = str_replace('/', '\/', $url);

            [78] => 		$url = preg_replace('/' . self :: numericLimitRegex . '/', '(?<$1>\d{$2})', $url);

            [79] => 		$url = preg_replace('/' . self :: numericRegex . '/', '(?<$1>\d+)', $url);

            [80] => 		$url = preg_replace('/' . self :: stringLimitRegex . '/', '(?<$1>[^$\/]{$2})', $url);

            [81] => 		$url = preg_replace('/' . self :: stringRegex . '/', '(?<$1>[^$\/]+)', $url);

            [82] => 		$url = preg_replace('/' . self :: wildcardRegex . '/', '.*?', $url);

            [83] => 

            [84] => 		self :: $urls[$route] = [$url, $module];

            [85] => 

            [86] => 		return $url;

            [87] => 	}

            [88] => 

            [89] => 	/**

            [90] => 	 * Add a route with its URL pattern and configuration data.

            [91] => 	 *

            [92] => 	 * @param string $url  The route URL pattern.

            [93] => 	 * @param array{module: string, edit?: string} $data  Route configuration data.

            [94] => 	 */

            [95] => 	public static function addRoute(string $url, array $data): void {

            [96] => 		self :: $routes[$url] = $data;

            [97] => 		self :: processRoute($url, $data);

            [98] => 	}

            [99] => 

            [100] => 	/**

            [101] => 	 * Remove one or more routes by URL pattern.

            [102] => 	 *

            [103] => 	 * @param string|array $url  A single URL pattern or array of URL patterns to remove.

            [104] => 	 */

            [105] => 	public static function removeRoute(string|array $url): void {

            [106] => 		if (is_array($url)) {

            [107] => 			foreach ($url as $route) {

            [108] => 				$module = self :: $urls[$route] ?? false;

            [109] => 

            [110] => 				if ($module) {

            [111] => 					unset(self :: $modules[$module]);

            [112] => 				}

            [113] => 				unset(self :: $urls[$route]);

            [114] => 			}

            [115] => 		} else {

            [116] => 			$module = self :: $urls[$url] ?? false;

            [117] => 

            [118] => 			if ($module) {

            [119] => 				unset(self :: $modules[$module]);

            [120] => 			}

            [121] => 			unset(self :: $urls[$url]);

            [122] => 		}

            [123] => 	}

            [124] => 

            [125] => 	/**

            [126] => 	 * Initialize routes from cache or config files.

            [127] => 	 *

            [128] => 	 * @param string $app  Application name.

            [129] => 	 * @param int|null $site_id  Optional site ID for site-specific routes.

            [130] => 	 * @return bool Always returns true.

            [131] => 	 */

            [132] => 	public static function init(string $app = 'app', ?int $site_id = null): bool {

            [133] => 		$cacheDriver = Cache :: getInstance();

            [134] => 		if ($site_id) {

            [135] => 			$cacheKey = $site_id;

            [136] => 		} else {

            [137] => 			$cacheKey = $app;

            [138] => 		}

            [139] => 

            [140] => 		if ($result = $cacheDriver->get('routes', $cacheKey)) {

            [141] => 			static::$routes  = $result['routes'];

            [142] => 			static::$urls    = $result['urls'];

            [143] => 			static::$modules = $result['modules'];

            [144] => 		} else {

            [145] => 			$routesConfig = DIR_ROOT . '/config/';

            [146] => 			if ($site_id) {

            [147] => 				$routesConfig .= "site-$site_id-routes.php";

            [148] => 			} else {

            [149] => 				$routesConfig .= "$app-routes.php";

            [150] => 			}

            [151] => 

            [152] => 			if (file_exists($routesConfig)) {

            [153] => 				self :: $routes += include $routesConfig;

            [154] => 			}

            [155] => 			list(self :: $routes) = Event::trigger(__CLASS__, __FUNCTION__ , self :: $routes);

            [156] => 

            [157] => 			foreach (self :: $routes as $url => $data) {

            [158] => 				self :: processRoute($url, $data);

            [159] => 			}

            [160] => 

            [161] => 			$result            = [];

            [162] => 			$result['routes']  = static::$routes;

            [163] => 			$result['modules'] = static::$modules;

            [164] => 			$result['urls']    = static::$urls;

            [165] => 

            [166] => 			$cacheDriver->set('routes', $cacheKey, $result);

            [167] => 		}

            [168] => 

            [169] => 		self :: $init = true;

            [170] => 

            [171] => 		return true;

            [172] => 	}

            [173] => 

            [174] => 	/**

            [175] => 	 * Match a URL against registered route patterns and return extracted parameters.

            [176] => 	 *

            [177] => 	 * @param string $url  The URL to match.

            [178] => 	 * @return array{route: string, module: string, pattern: string, [string]: string}|false  Matched parameters or false.

            [179] => 	 */

            [180] => 	public static function match(string $url): array|false {

            [181] => 		if (! self :: $init) {

            [182] => 			self :: init();

            [183] => 		}

            [184] => 		$url = preg_replace('/\?.*$/', '', str_replace('//', '/', $url));

            [185] => 

            [186] => 		foreach (self :: $urls as $route => $data) {

            [187] => 			$params                 = static::$routes[$route] ?? '';

            [188] => 			list($pattern, $module) = $data;

            [189] => 

            [190] => 			if ($url == $pattern || preg_match('/^' . $pattern . '$/', $url, $matches)) {

            [191] => 				$parameters = array_filter($matches, 'is_string', ARRAY_FILTER_USE_KEY);

            [192] => 

            [193] => 				$parameters['route']   = $route;

            [194] => 				$parameters['module']  = $module;

            [195] => 				$parameters['pattern'] = $pattern;

            [196] => 

            [197] => 				$parameters += $params;

            [198] => 

            [199] => 				return $parameters;

            [200] => 			}

            [201] => 		}

            [202] => 

            [203] => 		return false;

            [204] => 	}

            [205] => 

            [206] => 	/**

            [207] => 	 * Get the configuration data for a route by its pattern key.

            [208] => 	 *

            [209] => 	 * @param string $route  The route pattern key.

            [210] => 	 * @return array{module: string, edit?: string}  Route data or empty array.

            [211] => 	 */

            [212] => 	public static function get(string $route): array {

            [213] => 		return self :: $routes[$route] ?? [];

            [214] => 	}

            [215] => 

            [216] => 	/**

            [217] => 	 * Get all registered routes.

            [218] => 	 *

            [219] => 	 * @return array  All routes.

            [220] => 	 */

            [221] => 	public static function getRoutes(): array {

            [222] => 		return self :: $routes ?? [];

            [223] => 	}

            [224] => 

            [225] => 	/**

            [226] => 	 * Replace variable placeholders in a URL with parameter values.

            [227] => 	 *

            [228] => 	 * @param string $url  URL pattern with placeholders.

            [229] => 	 * @param array $parameters  Key-value pairs for replacement.

            [230] => 	 * @return string The URL with placeholders replaced.

            [231] => 	 */

            [232] => 	public static function varReplace(string $url, array $parameters): string {

            [233] => 		return preg_replace_callback('/' . self :: stringRegex . '|' . self :: numericRegex . '/',

            [234] => 			function (array $matches) use ($parameters): string {

            [235] => 				$var = $matches[1];

            [236] => 

            [237] => 				if (isset($parameters[$var])) {

            [238] => 					return $parameters[$var];

            [239] => 				}

            [240] => 

            [241] => 				return '';

            [242] => 			}, $url);

            [243] => 	}

            [244] => 

            [245] => 	/**

            [246] => 	 * Get route data for a module name.

            [247] => 	 *

            [248] => 	 * @param string $module  The module name.

            [249] => 	 * @return array, count: int}>  Route data for the module.

            [250] => 	 */

            [251] => 	public static function getRouteData(string $module): array {

            [252] => 		if (! self :: $init) {

            [253] => 			self :: init();

            [254] => 		}

            [255] => 

            [256] => 		return self :: $modules[$module] ?? [];

            [257] => 	}

            [258] => 

            [259] => 	/**

            [260] => 	 * Get URL data by matching against registered routes.

            [261] => 	 *

            [262] => 	 * @param string|false $url  The URL to resolve, or false to use current URL.

            [263] => 	 * @return array|false  Matched route parameters with route metadata, or false on no match.

            [264] => 	 */

            [265] => 	public static function getUrlData(string|false $url = false): array|false {

            [266] => 		if (! $url) {

            [267] => 			$url = \Vvveb\getCurrentUrl();

            [268] => 		}

            [269] => 

            [270] => 		$parameters = self :: match($url);

            [271] => 		$route      = stripslashes($parameters['route'] ?? '');

            [272] => 

            [273] => 		if ($parameters) {

            [274] => 			$parameters            = $parameters + self :: $routes[$route];

            [275] => 

            [276] => 			if (isset($parameters['edit'])) {

            [277] => 				$parameters['edit'] = self :: varReplace($parameters['edit'], $parameters);

            [278] => 			}

            [279] => 		}

            [280] => 

            [281] => 		return $parameters;

            [282] => 	}

            [283] => 

            [284] => 	/**

            [285] => 	 * Generate a URL from a module route name and parameters.

            [286] => 	 *

            [287] => 	 * @param string $route  The module/route name.

            [288] => 	 * @param array|false $parameters  Query parameters or false for no parameters.

            [289] => 	 * @return string|null  The resolved URL, or null if the route is not found.

            [290] => 	 */

            [291] => 	public static function url(string $route, array|false $parameters = false): ?string {

            [292] => 		if (! self :: $init) {

            [293] => 			self :: init();

            [294] => 		}

            [295] => 

            [296] => 		if (isset(self :: $modules[$route])) {

            [297] => 			$pattern    = self :: $modules[$route][0]['url'] ?? '';

            [298] => 

            [299] => 			if ($parameters && isset($parameters['route'])) {

            [300] => 				unset($parameters['route']);

            [301] => 			}

            [302] => 

            [303] => 			$parameters_count = is_array($parameters) ? count($parameters) : 0;

            [304] => 			$param_keys       = is_array($parameters) ? array_keys($parameters) : [];

            [305] => 

            [306] => 			foreach (self :: $modules[$route] as $value) {

            [307] => 				if ($value['parameters'] && $parameters_count) {

            [308] => 					if (count(array_intersect($value['parameters'], $param_keys)) === count($value['parameters'])) {

            [309] => 						$pattern  = $value['url'];

            [310] => 

            [311] => 						break;

            [312] => 					}

            [313] => 				} else {

            [314] => 					$no_parameters = $value['url'];

            [315] => 				}

            [316] => 			}

            [317] => 

            [318] => 			if (! $parameters) {

            [319] => 				$pattern = $no_parameters;

            [320] => 			}

            [321] => 

            [322] => 			$missing = false;

            [323] => 			$url     = preg_replace_callback('/' . self :: varRegex . '/',

            [324] => 				function (array $matches) use ($parameters, &$missing): string {

            [325] => 					$var = $matches[1];

            [326] => 

            [327] => 					if (isset($parameters[$var])) {

            [328] => 						return $parameters[$var];

            [329] => 					} else {

            [330] => 						$missing = true;

            [331] => 					}

            [332] => 

            [333] => 					return '';

            [334] => 				}, $pattern);

            [335] => 

            [336] => 			if ($missing) {

            [337] => 				return '/?route=' . $route . '&' . (is_array($parameters) ? http_build_query($parameters) : '');

            [338] => 			} else {

            [339] => 				return $url;

            [340] => 			}

            [341] => 		}

            [342] => 

            [343] => 		return null;

            [344] => 	}

            [345] => }

        )

)