我面临着我不知道如何实现函数的情况,我不确定什么是最好和更快的解决方案。
我有一个简单的路由对象,非常基本,我不需要这个特定项目的高级功能......它存储一个路由数组,唯一允许的方法是GET和POST,这大致是类结构:
class Router
{
// Array of Route Objects
private static $binded_routes = array();
// Method used to register a GET route.
public static function get() {}
// Method used to register a POST route.
public static function post() {}
// Other methods here like redirect(), routeTo(), dispatch()
}
路由可以声明如下:
Router::get('index', 'IndexController@method');
Router::get('users/{id}', 'UserController@showUser');
Router::get('route/to/something', 'Controller@method');
Router::get('route/to/something/{param1}', 'Controller@method1');
Router::get('route/to/something/{param1}/{param2}', 'Controller@method2');
存储 GET 路由的策略是这样的:
- 仅注册没有参数的路由(在本例中:索引、用户、路线/到/某物)
- 在指定参数的位置将它们存储为数组
- 不要存储具有相同数量参数的多个GET路由(在此示例中,声明"users/{test}"将引发错误)
路由对象如下所示:
class Route
{
private $route_type = 'GET';
private $route_name = null;
private $route_uri = null;
private $route_params = array();
private $route_controller = null;
private $route_method = null;
// Functions to correctly store and retrieve the above values
}
所以现在我在匹配 GET 请求时遇到问题,根据策略我可以做点什么诸如此类:
- 遍历所有绑定的路由。 找到完全匹配项,如果找到,则停止。
-> 因此,如果用户转到"路由/到/某物",我可以匹配第三条路由并将执行传递给正确的控制器。 - 如果未找到,请尽可能多地匹配路由,并将其余路径作为参数。
-> 因此,如果用户转到"route/to/something/1/2",我可以匹配"route/to/something"并将array(1,2)作为参数 - 现在我可以简单地计算参数的数量,并与路由进行比较,以找到唯一具有相同参数数量的路由。
目前,我想不出一种在没有多个foreach循环的情况下管理此过程的方法。最好的方法是什么?有没有办法构建正则表达式?以及如何生成它?
任何帮助将不胜感激,如果您需要更多信息,请告诉我。
经过一些编码,我设法创建了一个工作函数,棘手的部分是将 GET 请求与参数匹配。
例如,如果我有以下路线:
Router::get('user/{id}', 'UserController@showUser');
Router::get('route/path/{param1}', 'SomeController@someMethodA');
Router::get('route/path/{param1}/{param2}', 'SomeController@someMethodB');
用户可以通过浏览器发出请求,如下所示:
site.com/user/10
site.com/route/path/10
site.com/route/path/10/20
知道这一点,我的脚本必须通过以下方式识别(遵循有关如何解析 GET 请求的策略)请求的 URI:
route1: user
params: array(10)
route2: route/path
params: array(10)
route3: route/path
params: array(10,20)
以下是代码的相关部分:
$index = 0;
$array_of_matches = array();
// $current_uri is urldecoded route path
$splitted_uri = explode('/', $current_uri);
foreach (self::$binded_routes as $route)
{
if ($route->getURI() === $current_uri && !$route->hasParams())
{
// Gotcha.
$found_route = true;
$route_index = $index;
// No need to continue wasting time...
break;
}
$number_of_matches = 0;
$route_uri_split = explode('/', $route->getURI());
if ($splitted_uri[0] == $route_uri_split[0] && $route->hasParams())
{
$number_of_matches++;
// I need this to eliminate routes like
// users/list when searching for users/{1}
if (count($route_uri_split) > count($splitted_uri))
{
$number_of_matches = 0;
}
for($i = 1; $i < count($splitted_uri); $i++)
{
if (isset($route_uri_split[$i]))
{
if ($route_uri_split[$i] === $splitted_uri[$i])
$number_of_matches++;
else
$number_of_matches--;
}
}
$array_of_matches[$index] = $number_of_matches;
}
// Incrementing index for next array entry.
$index ++;
}
// Now try to find the route with the same amount of params if I still don't have a match.
if (!$found_route)
{
$highest_matches = array_keys($array_of_matches, max($array_of_matches));
foreach ($highest_matches as $match)
{
$matched_route = self::$binded_routes[$match];
$params_portion = ltrim(str_replace($matched_route->getURI(), '', $current_uri), '/');
// If $params_portion is empty it means that no params are passed.
$params_count = (empty($params_portion)) ? 0 : count(explode('/', $params_portion));
if ($params_count == $matched_route->paramsCount())
{
$found_route = true;
$route_index = $match;
$route_params = explode('/', $params_portion);
break;
}
}
}
if ($found_route)
{
// If params are needed set them now.
if (isset($route_params))
self::$binded_routes[$route_index]->setParams($route_params);
// Dispatch the route.
self::$binded_routes[$route_index]->dispatch();
}
else
{
// Route not found... redirect to 404 or error.
}
现在,我知道它看起来很丑,我想在可能的情况下改进这段代码。除了将代码提取到它自己的类上下文中,委托并使其更"甜蜜"之外,也许它可以做得更快、更高效或更智能。
如果你有一些想法,请告诉我。