186 lines
5.4 KiB
PHP
186 lines
5.4 KiB
PHP
<?php
|
|
|
|
const ROUTER_VERSION = 1;
|
|
const ROUTER_MC_KEY = 'ch1p/routes';
|
|
|
|
$RouterInput = [];
|
|
$Routes = [
|
|
'children' => [],
|
|
'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;
|
|
}
|