onnerby/doeroute

快速直观的路由器

2.1.2 2021-04-14 09:39 UTC

This package is auto-updated.

Last update: 2024-09-14 17:00:57 UTC


README

A fast and intuitive PHP Router. Don't make things more complicated than they needs to be. But maybe a little more flexible than this awesome simple router

\Doe\Router

The Doe\Router is a (single file) router where you build your routes using subpaths and closures. The advantage is that the closures are only called if the subpath match which makes it SUPER FAST and easy to follow. It also makes it very easy to delegate specific paths to some kind of controller/action-pattern. After I wrote Doe\Router I found FastRoute that is really fast and very similar to this router and probably a bit more flexible when it comes to multiple variables embedded in the path. They are similar in many ways, but the pattern is slightly different with both pros and cons.

安装

composer require onnerby/doeroute

基本示例

$router = new \Doe\Router(['GET', 'POST']);
$router->path('blog', function($router) {
	// This closure is called if the route starts with /blog

	$router->path('list', 'GET', function ($router) {
		// This is returned when route goes to "/blog/list"
		return "List of all posts";
	});
	$router->path('tags', 'GET', function ($router) {
		// This is returned when route goes to "/blog/tags"
		return "List of all tags";
	});
	$router->pathVariable('/^([0-9]+)$/', function ($router, $postId) {
		// This is returned when route goes to something like "/blog/1234"
		return "Post " . $postId;
	});
});

// Find route and output the results
echo $router->route($_SERVER['REQUEST_METHOD'], $_SERVER['DOCUMENT_URI']);

控制器示例

If you are building bigger webapps you may like to delegate routes to some kind of controller. The Doe\Router is not connected to any kind of pattern for this - but it's still super simple to delegate the route.

// Main app
$router = new \Doe\Router(['GET', 'POST']);
// Route everything starting with /project to our \Controller_Project::route
$router->path('project', ['Controller_Project', 'route']);

...

控制器

class Controller_Project
{
	public static function route($router)
	{
		$controller = new self;
		
		$router->path('list', 'GET', [$controller, 'list'] );

		$router->pathVariable('/^([0-9]+)$/', function ($router, $projectId) use ($controller) {

			// Any generic method needed for everything
			$controller->getProject($projectId);	

			$router->path('overview', 'GET', [$controller, 'overview'] );
			$router->path('save', 'POST', [$controller, 'save'] );

		});

		// Lets also map the "/project" path to the controllers "list" action
		$router->pathEmpty('GET', [$controller, 'list']);

		$router->pathNotFound([$controller, 'error']);

	}

	private function getProject($projectId) { /* Get the project somehow from a database? */ }

	public function list($router)
	{
		// Full path to this route is "/project/list"
		return 'List projects';
	}

	public function overview($router, $projectId)
	{
		// Full path to this route is "/project/1234/overview"
		return 'Project ' . $projectId . ' overview';
	}

	public function save($router, $projectId)
	{
		// Full path to this route is "/project/1234/save"
		return 'Saved project ' . $projectId;
	}

	public function error($router)
	{
		// Anything not found under "/project/xxxxx"
		return 'You look lost. How can I help?';
	}


}

过滤器

You may also use filters to execute stuff before the routes.

// In main app
function authorize($router, $verb) {
	// Authorize user somehow
	if (!($user = getUser())) {
		// Returning anything in "before"-filters will interrupt the route.
		return 'You do not have access to this area.';
	}
}

$router = new \Doe\Router(['GET', 'POST']);
$router->filter('authorize', function($router) {
	$router->path('restrictedarea', function ($router) {
		return "Warning: Resticted area. Authorized personnel only.";
	});
});
...

为什么还需要另一个路由器?

我使用的大多数路由器都过于复杂。路由器用于Web上解析URL路径到某种操作。所以让我们就做这件事。

A URL 是由 / 分隔的,而大多数路由器会一次性定义所有路径的路线,但我决定一次解析一个段。我的意思是——你最终可能需要在每个段上做些事情。例如,如果我构建以下路线

  • /user 列出所有用户
  • /user/123 显示用户个人资料页面
  • /user/123/badges 显示包含用户“徽章”的个人资料页面
  • /user/123/projects 显示包含用户项目的个人资料页面

等等,等等。你不会直接定义 /user/[0-9]+/project,而是会定义从不同段到完整路径的各个部分。

这种方法的优点是我们不需要为每个请求定义所有路线。相反,我们寻找第一个段,一旦找到,就解析下一个段。还有优点是,所有在 /user/[0-9]+ 内的内容都有自己的可调用项,我们可以检查在发送路由到下一个段之前用户是否有权访问。