From 2d1a7ec432ad7c92753eebf353ae1dc9fac49667 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Fri, 12 Jun 2020 23:19:23 +0900 Subject: [PATCH] Add ability to parse routes into regexp --- .../framework/parsers/moduleactionparser.php | 84 ++++++++++++++++++- 1 file changed, 81 insertions(+), 3 deletions(-) diff --git a/common/framework/parsers/moduleactionparser.php b/common/framework/parsers/moduleactionparser.php index 158ae958d..94437ea96 100644 --- a/common/framework/parsers/moduleactionparser.php +++ b/common/framework/parsers/moduleactionparser.php @@ -7,6 +7,19 @@ namespace Rhymix\Framework\Parsers; */ class ModuleActionParser { + /** + * Shortcuts for route definition. + */ + protected static $_shortcuts = array( + 'int' => '[1-9][0-9]*', + 'float' => '[0-9]+(?:\.[0-9]+)?', + 'number' => '[0-9]+', + 'alpha' => '[a-zA-Z]+', + 'alphanum' => '[a-zA-Z0-9]+', + 'hex' => '[0-9a-f]+', + 'word' => '[a-zA-Z0-9_]+', + ); + /** * Load an XML file. * @@ -31,6 +44,9 @@ class ModuleActionParser $info->default_index_act = ''; $info->setup_index_act = ''; $info->simple_setup_index_act = ''; + $info->route = new \stdClass; + $info->route->GET = []; + $info->route->POST = []; $info->action = new \stdClass; $info->menu = new \stdClass; $info->grant = new \stdClass; @@ -47,7 +63,7 @@ class ModuleActionParser $info->grant->{$grant_name} = $grant_info; } - // Parse permissions. + // Parse permissions not defined in the section. foreach ($xml->permissions->permission as $permission) { $action_name = trim($permission['action']); @@ -75,6 +91,7 @@ class ModuleActionParser // Parse actions. foreach ($xml->actions->action as $action) { + // Parse permissions. $action_name = trim($action['name']); $permission = trim($action['permission']); if ($permission) @@ -88,16 +105,42 @@ class ModuleActionParser $info->permission_check->{$action_name}->type = trim($action['check_type']) ?: trim($action['check-type']); } + // Parse routes. + $route = trim($action['route']); + $method = trim($action['method']); + $route_vars = []; + if ($route) + { + $route_info = self::analyzeRoute($route); + $route_vars = $route_info->vars; + if ($method) + { + $methods = explode('|', strtoupper($method)); + } + else + { + $methods = starts_with('proc', $action_name) ? ['POST'] : ['GET']; + } + foreach ($methods as $method) + { + $info->route->{$method}[$route_info->regexp] = $action_name; + } + } + + // Parse other information about this action. $action_info = new \stdClass; $action_info->type = trim($action['type']); $action_info->grant = trim($action['grant']) ?: 'guest'; - $action_info->standalone = trim($action['standalone']) === 'false' ? 'false' : 'true'; $action_info->ruleset = trim($action['ruleset']); - $action_info->method = trim($action['method']); + $action_info->method = $method; + $action_info->route = $route; + $action_info->route_vars = $route_vars; + $action_info->standalone = trim($action['standalone']) === 'false' ? 'false' : 'true'; $action_info->check_csrf = (trim($action['check_csrf']) ?: trim($action['check-csrf'])) === 'false' ? 'false' : 'true'; $action_info->meta_noindex = (trim($action['meta_noindex']) ?: trim($action['meta-noindex'])) === 'true' ? 'true' : 'false'; $info->action->{$action_name} = $action_info; + // Set the menu name and index settings. $menu_name = trim($action['menu_name']); if ($menu_name) { @@ -129,6 +172,41 @@ class ModuleActionParser return $info; } + /** + * Convert route definition into a regular expression. + * + * @param string $route + * @return object + */ + public static function analyzeRoute(string $route): object + { + // Replace variables in the route definition into appropriate regexp. + $var_regexp = '#\\$([a-z0-9_]+)(?::(' . implode('|', array_keys(self::$_shortcuts)) . '))?#i'; + $vars = array(); + $regexp = preg_replace_callback($var_regexp, function($match) use(&$vars) { + if (isset($match[2]) && isset(self::$_shortcuts[$match[2]])) + { + $var_pattern = self::$_shortcuts[$match[2]]; + } + else + { + $var_pattern = ends_with('_srl', $match[1]) ? '[0-9]+' : '[^/]+'; + } + $named_group = '(?P<' . $match[1] . '>' . $var_pattern . ')'; + $vars[] = $match[1]; + return $named_group; + }, $route); + + // Anchor the regexp at both ends. + $regexp = '#^' . strtr($regexp, ['#' => '\\#']) . '$#u'; + + // Return the regexp and variable list. + $result = new \stdClass; + $result->regexp = $regexp; + $result->vars = $vars; + return $result; + } + /** * Get child elements that match a language. *