summaryrefslogtreecommitdiff
path: root/engine/router.php
diff options
context:
space:
mode:
authorEvgeny Zinoviev <me@ch1p.io>2024-01-31 06:11:00 +0300
committerEvgeny Zinoviev <me@ch1p.io>2024-01-31 20:45:40 +0300
commitc0dc531ebefd8912819f3b6c8bda1fed3c7e750c (patch)
tree2c75aa9df182260aef09faf4befd81a4c2b9c5e2 /engine/router.php
parent48d688cdf7f9eae1bf11b8a6f0e5b98687c604cb (diff)
make it simple, but not simpler
Diffstat (limited to 'engine/router.php')
-rw-r--r--engine/router.php185
1 files changed, 185 insertions, 0 deletions
diff --git a/engine/router.php b/engine/router.php
new file mode 100644
index 0000000..401e177
--- /dev/null
+++ b/engine/router.php
@@ -0,0 +1,185 @@
+<?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;
+}