[], 're_children' => [] ]; function router_init(): void { global $Routes; $mc = MC(); $from_cache = !is_dev(); $write_cache = !is_dev(); if ($from_cache) { $cache = $mc->get(ROUTER_MC_KEY); if ($cache === false || !isset($cache['version']) || $cache['version'] < ROUTER_VERSION) { $from_cache = false; } else { $Routes = $cache['routes']; } } if (!$from_cache) { $routes_table = require_once APP_ROOT.'/routes.php'; foreach ($routes_table as $controller => $routes) { foreach ($routes as $route => $resolve) router_add($route, $controller.' '.$resolve); } if ($write_cache) $mc->set(ROUTER_MC_KEY, ['version' => ROUTER_VERSION, 'routes' => $Routes]); } } function router_add(string $template, string $value): void { global $Routes; if ($template == '') return; // expand {enum,erat,ions} $templates = [[$template, $value]]; if (preg_match_all('/\{([\w\d_\-,]+)\}/', $template, $matches)) { foreach ($matches[1] as $match_index => $variants) { $variants = explode(',', $variants); $variants = array_map('trim', $variants); $variants = array_filter($variants, function($s) { return $s != ''; }); for ($i = 0; $i < count($templates); ) { list($template, $value) = $templates[$i]; $new_templates = []; foreach ($variants as $variant_index => $variant) { $new_templates[] = [ str_replace_once($matches[0][$match_index], $variant, $template), str_replace('${'.($match_index+1).'}', $variant, $value) ]; } array_splice($templates, $i, 1, $new_templates); $i += count($new_templates); } } } // process all generated routes foreach ($templates as $template) { list($template, $value) = $template; $start_pos = 0; $parent = &$Routes; $template_len = strlen($template); while ($start_pos < $template_len) { $slash_pos = strpos($template, '/', $start_pos); if ($slash_pos !== false) { $part = substr($template, $start_pos, $slash_pos-$start_pos+1); $start_pos = $slash_pos+1; } else { $part = substr($template, $start_pos); $start_pos = $template_len; } $parent = &_router_add($parent, $part, $start_pos < $template_len ? null : $value); } } } function &_router_add(&$parent, $part, $value = null) { $par_pos = strpos($part, '('); $is_regex = $par_pos !== false && ($par_pos == 0 || $part[$par_pos-1] != '\\'); $children_key = !$is_regex ? 'children' : 're_children'; if (isset($parent[$children_key][$part])) { if (is_null($value)) { $parent = &$parent[$children_key][$part]; } else { if (!isset($parent[$children_key][$part]['value'])) { $parent[$children_key][$part]['value'] = $value; } else { trigger_error(__METHOD__.': route is already defined'); } } return $parent; } $child = [ 'children' => [], 're_children' => [] ]; if (!is_null($value)) $child['value'] = $value; $parent[$children_key][$part] = $child; return $parent[$children_key][$part]; } function router_find($uri) { global $Routes; if ($uri != '/' && $uri[0] == '/') $uri = substr($uri, 1); $start_pos = 0; $parent = &$Routes; $uri_len = strlen($uri); $matches = []; while ($start_pos < $uri_len) { $slash_pos = strpos($uri, '/', $start_pos); if ($slash_pos !== false) { $part = substr($uri, $start_pos, $slash_pos-$start_pos+1); $start_pos = $slash_pos+1; } else { $part = substr($uri, $start_pos); $start_pos = $uri_len; } $found = false; if (isset($parent['children'][$part])) { $parent = &$parent['children'][$part]; $found = true; } else if (!empty($parent['re_children'])) { foreach ($parent['re_children'] as $re => &$child) { $exp = '#^'.$re.'$#'; $re_result = preg_match($exp, $part, $match); if ($re_result === false) { logError(__METHOD__.": regex $exp failed"); continue; } if ($re_result) { if (count($match) > 1) $matches = array_merge($matches, array_slice($match, 1)); $parent = &$child; $found = true; break; } } } if (!$found) return null; } if (!isset($parent['value'])) return null; $value = $parent['value']; if (!empty($matches)) { foreach ($matches as $i => $match) { $needle = '$('.($i+1).')'; $pos = strpos($value, $needle); if ($pos !== false) $value = substr_replace($value, $match, $pos, strlen($needle)); } } return $value; }