如何使用 Lumen 或 Slim 框架实现默认路由行为


How to implement a default routing behaviour with Lumen or Slim framework

我正在考虑在项目中使用 Lumen 或 Slim,并想知道是否可以基于目录结构自动加载控制器,而不必注册所有路由。

这就是我希望自动加载的工作方式.

示例目录/类结构:

 /app/Http/Controllers/
    Foo/
        BarController.php        # App'Http'Controllers'Foo'BarController

如果路线是

example.com/foo/bar == App'Http'Controllers'Foo'BarController::index()

example.com/foo/bar/add == App'Http'Controllers'Foo'BarController::add()

注册的路由应优先于自动加载的类。

我找到了一种基于 Opencart 路由方法的方法。他们在396个控制器上有1145个不同的公共方法,这些方法都是在没有明确说明控制器和方法的情况下调用的。这是我的尝试。

.htaccess

RewriteRule ^([^?]*) index.php?route=$1 [L,QSA]

app/Http/routes.php

$route = array_shift($_GET);
$method_name = '';
$parts = explode('/', preg_replace('/[^a-zA-Z0-9_'/]/', '', (string)$route));
while ($parts) {
    $class = ''App'Http'Controllers''' . implode('''', $parts);
    if (class_exists($class)){
        $app->match($route, $class . '@' . method_exists($class, $method_name) ? $method_name : 'index');
        break;
    } else {
        $method_name = array_pop($parts);
    }
}

如果需要与默认 Opencart 不同的路由,请使用 .htaccess RewriteRuleresponse->redirect路由到备用控制器。

我会使用他们的方法,但说明我的路线覆盖 app/Http/routes.php .这样

// route overrides 
$app->get('/', 'common/home@index');
$app->get('/home', 'common/home@index');

我是否正确认为这将使应用程序运行得更快,因为它不必搜索所有注册的路由以查找匹配项?

有没有更好的方法来执行此自动路由过程?

我认为你可以结合反射和 Slim 3 对使用控制器方法而不是闭包定义路由的支持来做到这一点。

基本策略如下:

  1. 搜索每个控制器类(使用 glob 或自动加载器);
  2. 对于每个类,使用 ReflectionMethod::IS_PUBLIC 筛选器调用 ReflectionClass::getMethods ,以便仅获取该类的公共方法;
  3. 使用
  4. ReflectionClass::getName 获取类名,使用 ReflectionClass::getNamespaceName 获取命名空间(如有必要);
  5. 从命名空间、类名和方法名构建路由签名,也许使用 https://github.com/cocur/slugify 这样的 slugization 库;
  6. 生成相应的路由$app->get($route_signature, "$class_name:$method_name")

这是一个有趣的想法,尽管您需要非常小心,以免意外地暴露任何您不希望客户端直接访问的方法。 其他一些注意事项:

  • 反射非常慢,因此您可能希望将其实现为更多的构建步骤,缓存生成的路由,而不是在每个请求中动态重新生成它们。
  • 您可能需要一些额外的命名约定来区分 HTTP 谓词。 例如,与GET路由对应的所有方法名称开头都带有 get 。 所以你可能有'Foo'BarController::getAdd'Foo'BarController::postAdd等。
  • 构造参数化路由(/bar/add/{id})将需要更多的工作,因为您可能希望使用ReflectionFunctionAbstract::getParameters提取相应的方法参数。 同样,您需要决定如何根据这些参数构造路由的一些约定。