我正在考虑在项目中使用 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 RewriteRule
或response->redirect
路由到备用控制器。
我会使用他们的方法,但说明我的路线覆盖 app/Http/routes.php
.这样
// route overrides
$app->get('/', 'common/home@index');
$app->get('/home', 'common/home@index');
我是否正确认为这将使应用程序运行得更快,因为它不必搜索所有注册的路由以查找匹配项?
有没有更好的方法来执行此自动路由过程?
我认为你可以结合反射和 Slim 3 对使用控制器方法而不是闭包定义路由的支持来做到这一点。
基本策略如下:
- 搜索每个控制器类(使用
glob
或自动加载器); - 对于每个类,使用
ReflectionMethod::IS_PUBLIC
筛选器调用ReflectionClass::getMethods
,以便仅获取该类的公共方法;
使用 ReflectionClass::getName
获取类名,使用ReflectionClass::getNamespaceName
获取命名空间(如有必要);- 从命名空间、类名和方法名构建路由签名,也许使用 https://github.com/cocur/slugify 这样的 slugization 库;
- 生成相应的路由
$app->get($route_signature, "$class_name:$method_name")
。
这是一个有趣的想法,尽管您需要非常小心,以免意外地暴露任何您不希望客户端直接访问的方法。 其他一些注意事项:
- 反射非常慢,因此您可能希望将其实现为更多的构建步骤,缓存生成的路由,而不是在每个请求中动态重新生成它们。
- 您可能需要一些额外的命名约定来区分 HTTP 谓词。 例如,与
GET
路由对应的所有方法名称开头都带有get
。 所以你可能有'Foo'BarController::getAdd
、'Foo'BarController::postAdd
等。 - 构造参数化路由(
/bar/add/{id}
)将需要更多的工作,因为您可能希望使用ReflectionFunctionAbstract::getParameters
提取相应的方法参数。 同样,您需要决定如何根据这些参数构造路由的一些约定。