jetfirephp/routing

JetFire - 路由

v1.3 2016-05-31 18:08 UTC

This package is not auto-updated.

Last update: 2024-09-14 18:36:48 UTC


README

SensioLabsInsight Build Status Scrutinizer Code Quality

A simple & powerful router for PHP 5.4+

特性

V1.3

V1.2

V1.1

V1.0

入门

  1. 需要PHP 5.4+
  2. 使用Composer安装JetFire\Routing
  3. 设置URL重写,以便所有请求都由index.php处理

安装

通过composer

$ composer require jetfirephp/routing

.htaccess

RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.[a-zA-Z0-9\-\_\/]*)$ index.php?url=$1 [QSA,L]

用法

创建JetFire\Routing\RouteCollection实例并定义您的路由。然后创建JetFire\Routing\Router实例并运行您的路由。

// Require composer autoloader
require __DIR__ . '/vendor/autoload.php';

// Create RouteCollection instance
$collection = new \JetFire\Routing\RouteCollection();

// Define your routes
// ...

// Create an instance of Router
$router = new \JetFire\Routing\Router($collection)

// select your matcher
$matcher1 =  new \JetFire\Routing\Matcher\ArrayMatcher($router);
$matcher2 =  new \JetFire\Routing\Matcher\UriMatcher($router);

// set your matcher to the router
$router->setMatcher([$matcher1,$matcher2])

// Run it!
$router->run();

匹配器

JetFire\Routing为您的路由提供两种类型的匹配器:JetFire\Routing\Matcher\ArrayMatcherJetFire\Routing\Matcher\UriMatcher

Uri匹配器

使用Uri匹配器,您不需要定义路由。根据uri,它可以检查当前url是否存在目标。但您必须将视图目录路径和控制器命名空间定义为集合

$options = [
    'view_dir' => '_VIEW_DIR_PATH_',
    'ctrl_namespace' => '_CONTROLLERS_NAMESPACE_'
];
$collection = new \JetFire\Routing\RouteCollection(null,$options);
// or
$collection = new \JetFire\Routing\RouteCollection();
$collection->setOption($options);
// or
$collection = new \JetFire\Routing\RouteCollection();
$collection->addRoutes(null,$options)

例如,如果uri是:/home/index

解析器

以下是Uri匹配器解析器的列表

$resolver = [
    'isControllerAndTemplate',
    'isController',
    'isTemplate'
];
模板解析器

Uri匹配器检查/_VIEW_DIR_PATH_/Home目录中是否存在index.php文件。

如果您想检查其他扩展(html、json等),可以像这样配置路由

$router->setConfig([
	
	// Define your template extension like this
	'templateExtension' => ['.php','.html','.twig','.json','.xml'],

]);
控制器解析器

使用控制器解析器,Uri匹配器检查命名空间_CONTROLLERS_NAMESPACE_中的控制器HomeController是否有index方法。您必须在匹配之前要求控制器,或者可以使用自定义自动加载器来加载控制器。Uri匹配器还支持动态路由。例如,如果uri是:/home/user/peter/parker,则必须有一个方法user,它有两个参数如下所示

class HomeController {
    public function user($firstName,$lastName){
        // $firstName = peter
        // $lastName = parker
    }
}

数组匹配器

使用数组匹配器,您必须像这样添加路由

$options = [
    'view_dir' => '_VIEW_DIR_PATH_',
    'ctrl_namespace' => '_CONTROLLERS_NAMESPACE_'
];

// addRoutes expect an array argument 
$collection->addRoutes([
	'/home/index' => '_TARGET_'	
],$options);

// or a file containing an array
$collection->addRoutes('path_to_array_file',$options);

我们建议您在单独的文件中定义路由,并将路径传递给addRoutes()方法。

// routes.php file
return [
	'/home/index' => '_TARGET_'
];
解析器

以下是Uri匹配器解析器的列表

$resolver = [
    'isControllerAndTemplate',
    'isClosureAndTemplate',
    'isClosure',
    'isController',
    'isTemplate'
];

您有5种可能的数组路由操作。我们假设您正在使用单独的路由文件。

模板解析器
return [
	
	// static route
	'/home/index' => 'Home/index.php',
	
	// dynamic route with arguments
	'/home/user-:id-:slug' => [
		'use' => 'Home/page.html',
		'arguments' => ['id' => '[0-9]+','slug' => '[a-z-]*'],
	],

];

控制器解析器
return [
	
	// static route
	'/home/index' => 'HomeController@index',

	// dynamic route with arguments
	'/home/user-:id-:slug' => [
		'use' => 'HomeController@page',
		'arguments' => ['id' => '[0-9]+','slug' => '[a-z-]*'],
	],
	
];

控制器和模板解析器
return [
	
	// controller and template resolver
	// call first the controller and render then the template
	// if the template is not found, the controller is returned
	'/home/log' => [
	    'use' => 'HomeController@log',
	    'template' => 'Home/log.php', //in your controller you can return an array of data that you can access in your template
	],
	
	// dynamic route with arguments
    '/home/user-:id-:slug' => [
        'use' => 'HomeController@page',
        'template' => 'Home/log.php',
        'arguments' => ['id' => '[0-9]+','slug' => '[a-z-]*'],
    ],

];

控制器分组解析器
return [

	// suppose we have the following methods in the AccountController :
	// public function create();
	// public function read($id);
	// public function update($id);
	// public function destroy($id);
	// if the uri is /account/create the router will call the associated method in the controller
	'/account/*' => [
	    'use' => 'AccountController@{method}',
	],

];

Closure解析器
return [
		
	// static route
	'/home/index' => function(){
		return 'Hello world !';
	},
	
	// dynamic route with arguments
	'/home/user-:id-:slug' => [
		'use' => function($id,$slug){
			return 'Hello User '.$id.'-'.$slug;
		},
		'arguments' => ['id' => '[0-9]+','slug' => '[a-z-]*'],
	],
	
];

Closure和模板解析器
return [
	
	// closure and template matching
    // call first the closure and render then the template
    '/home/log' => [
        'use' => function(){
            return ['name' => 'Peter'];
        }
        'template' => 'Home/log.php', // in log.php you can access the return data like this : $name ='Peter'
    ],
    
    '/home/user-:id-:slug' => [
        'use' => function($id,$slug){
            return ['id' => $id,'slug' => $slug];
        },
        'template' => 'Home/log.php',
        'arguments' => ['id' => '[0-9]+','slug' => '[a-z-]*'],
    ],
    
];

块路由

使用JetFire\Routing,您有创建块路由以更好地组织代码的能力。例如,如果您有一个网站的行政部分,您可以为此部分创建一个块,并为公共部分创建另一个块,如下所示

// Create RouteCollection instance
$collection = new \JetFire\Routing\RouteCollection();

// Block routes
$collection->addRoutes('admin_routes_path',['view_dir' => 'admin_view_path' , 'ctrl_namespace' => 'admin_controllers_namespace','prefix' => 'admin']);
$collection->addRoutes('public_routes_path',['view_dir' => 'public_view_path' , 'ctrl_namespace' => 'public_controllers_namespace']);

// Create an instance of Router
$router = new \JetFire\Routing\Router($collection)
// Select your matcher
$router->addRouter(new \JetFire\Routing\Matcher\ArrayMatcher($router));

// Run it!
$router->run();

路由配置

以下是您可以编辑的路由配置列表

$router->setConfig([

	// You can add/remove extension for views
	// default extension for views
	'templateExtension'      => ['.html', '.php', '.json', '.xml'],

	// If you use template engine library, you can use this to render the view
	// See the 'Integration with other libraries' section for more details
	'templateCallback'       => [],
	
	// If you want to add a dependency injection container for your controllers constructor or method
	// for example if your controller 'HomeController' method 'log' method require a class like this : public function log(Request $request)
	// by default :
	'di'                => function($class){
	                            return new $class;
	                       },

	// See the Named Routes section for more details
	'generateRoutesPath' => false,
]);

集合选项

以下是您可以编辑的每个集合路由的选项列表

$options = [
    // your view directory
    'view_dir' => 'view_directory',
    // your controllers namespace
    'ctrl_namespace' => 'controllers_namespace',
    // your routes prefix
    'prefix' => 'your_prefix'
];

命名路由

您可以为每个路由指定一个名称,如下所示

return [
	
	'/home/index' => [
		'use' => 'Home/index.html',
		'name' => 'home.index'
	],

	'/home/user-:id-:slug' => [
		'use' => 'HomeController@user',
		'name' => 'home.user',
		'arguments' => ['id' => '[0-9]+','slug' => '[a-z-]*'],
	],
];

然后要获取此路由的URL,您可以这样做

// You have to enable generateRoutesPath to get routes url
$router->setConfig([
	'generateRoutesPath' => true,
	// Other configuration
	// ...
]);

// Reverse routing
$collection->getRoutePath('home.index'); // return http://your_domain/home/index
$collection->getRoutePath('home.user',[ 'id' => 1, 'slug' => 'toto']); // return http://your_domain/home/user-1-toto

仅支持在 JetFire\Routing\Matcher\ArrayMatcher 中。

REST 路由

您可以通过这种方式为每个路由指定请求方法

return [
	
	'/api/users' => [
		'use' => [
		    'GET' => function($response){
		        $response->setHeaders(['Content-Type' => 'application/json']);
		        return ['name' => 'Peter'];
		    },
		    'POST' => function($response){
                $response->setHeaders(['Content-Type' => 'application/json']);
                $response->setStatusCode(201);
                return [];
            },
		],
		'name' => 'api.users',
		'method' => ['GET', 'POST'] 
	],

	'/api/users/:id' => [
		'use' => [
		    'GET' => function($response){
                $response->setHeaders(['Content-Type' => 'application/json']);
                return ['name' => 'Peter'];
            },
            'PUT' => function($response){
                $response->setHeaders(['Content-Type' => 'application/json']);
                return [];
            },
		],
		'name' => 'api.user',
		'arguments' => ['id' => '[0-9]+'],
		'method' => ['GET','PUT'] 
	],
];

前缀

您可以为每个路由集合设置前缀,如下所示

$collection->addRoutes('routes_file_1',['prefix' => 'prefix_1']); // all routes in routes_file_1 begin with prefix_1/
$collection->addRoutes('routes_file_2',['prefix' => 'prefix_2']); // all routes in routes_file_2 begin with prefix_2/

或者

$collection->addRoutes('routes_file_1'); 
$collection->addRoutes('routes_file_2'); 
$collection->setPrefix(['prefix_1','prefix_2']);

中间件

中间件在路由匹配当前 URI 前后调用。您必须创建一个中间件配置文件,如下所示

// Your middleware file
return [
	
	// global middleware are called every time
    'global_middleware' => [    
    	// Here you define your middleware class to be called
        'app\Middleware\Global',
    ],

	// block middleware are called when the current route block match one of the following block
    'block_middleware' => [
    	// You define here for each block the middleware class to be called
        '/app/Blocks/PublicBlock/' => 'app\Middleware\Public',
        '/app/Blocks/AdminBlock/' => 'app\Middleware\Admin',
        '/app/Blocks/UserBlock/' => 'app\Middleware\User',
    ],
	
	// class middleware are called when the controller router match one of the following controller
    'class_middleware' => [
    	// You define here for each controller the middleware class to be called
        '/app/Blocks/PublicBlock/Controllers/HomeController' => 'app\Middleware\Home',
    ],

	// route middleware are called when the current route match one of the following middleware name
    'route_middleware' => [
    	// You define here a name to the middleware and assign the class to be called
    	// You have to specify this name to the route like this : `'middleware' => 'home'`
        'home' => 'app\Middleware\App'
    ],

];

然后您必须以这种方式实例化中间件类 Middleware

$middleware = new Middleware($router);
$middleware->setCallbackAction('before', 'your_before_middleware_file');
$middleware->setCallbackAction('between', 'your_between_middleware_file');
$middleware->setCallbackAction('after', 'your_after_middleware_file');

$router->addMiddleware($middleware);

让我们看看如何创建您的中间件类。例如,我们以全局中间件为例

namespace app\Middleware;

class Global{

	// Middleware class must implements handle method
	// object passed in argument will be inject automatically
	public function handle(){
		// here you put your code
		// ...
	}
}

查看 API 部分,了解如何在中间件类中处理您的 $route。

自定义匹配器和调度器

如果默认的匹配器和调度器不符合您的预期,您可以像这样编写自己的匹配器和调度器

class MyCustomMatcher implements MatcherInterface{
    
    public function __construct(Router $router);

    // in this method you can check if the current uri match your expectation
    // return true or false
    // if it match you have to set your route target with an array of params and the dispatcher class name to be called
    // $this->setTarget(['dispatcher' => '\My\Custom\Dispatcher\Class\Name', 'other_params' => 'values']);
    public function match();
    
    // set your route target $this->router->route->setTarget($target);
    public function setTarget($target = []);

    // set your resolver
    public function setResolver($resolver = []);
    
    // you can add multiple resolver method in the same matcher
    public function addResolver($resolver);
    
    // to retrieve your resolver
    public function getResolver();
    
    // dispatcher yo be called
    public function setDispatcher($dispatcher = []);
    
    public function addDispatcher($dispatcher);
}

class MyCustomDispatcher implements DispatcherInterface{
   
    public function __construct(Router $router);
       
    // your target to call
    // you can get your route target information with $this->route->getTarget()
    public function call();
}

$router->addMatcher('MyCustomMatcher');

您也可以像这样覆盖默认的匹配器

class MyCustomMatcher extends ArrayMatcher implements MatcherInterface{
    
    public function __construct(Router $router){
        parent::__construct($router);
        // your custom match method
        $this->addResolver('customResolver');
    }

    public function customResolver(){
        // your code here
        // ...
        // then you set the route target with the dispatcher
    }
}

class MyCustomDispatcher implements DispatcherInterface{
   
    public function __construct(Router $router);
       
    // your target to call
    // you can get your route target information with $this->route->getTarget()
    public function call();
}

与其他库集成

如果您想集成其他模板引擎库(如 twig、smarty 等),您必须在路由中设置 'templateCallback'。

// Twig template engine
require_once '/path/to/lib/Twig/Autoloader.php';
Twig_Autoloader::register();

// Other template engine
$tpl = new \Acme\Template\Template();

$router->setConfig([
	'templateCallback' => [

		// if the router find a template with twig enxtension then it will call the twig template engine
		'twig' => function($route){				
			$loader = new Twig_Loader_Filesystem($route->getTarget('block'));
			$twig = new Twig_Environment($loader, []);
			$template = $twig->loadTemplate($route->getTarget('template'));
			echo $template->render($route->getData());
		},
	
		// for other template engine
		'tpl' => function($route) use ($tpl){
			$tpl->load($route->getTarget('template'));
			$tpl->setData($route->getData());
			$tpl->display();
		}
	],

	// Other configuration
	// ...
]);

子域名

return [
    '{subdomain}.{host}/home' => [
         'use' => 'AdminController@index',
         'name' => 'admin.home.index',
         'subdomain' => 'admin' // could be a regex for multiple subdomain
    ]
];

或者,如果您想为某个块添加子域名,您必须将此行添加到您的路由集合选项中

$options = [
    // ...
    'subdomain' => 'your_subdomain'
];

API

以下是您最可能使用的公共类中的公共方法和变量的列表。

// JetFire\Routing\RouteCollection
$collection->
	routesByName								// routes url by their name
	countRoutes									// count routes block
	addRoutes($collection,$options = [])		// set your routes
	getRoutes($key = null)						// return all routes
	getRoutePath($name,$params = []) 			// return the url of route	
	setPrefix($prefix)							// $prefix can be a string (applied for every collection) 
												// or an array (for each collection you can specify a prefix)
    setOption($options = [])                    // set your routes option												
	generateRoutesPath() 						// generate routes url by their name

// JetFire\Routing\Router
$router->
	response							        // JetFire\Routing\ResponseInterface instance
	route										// JetFire\Routing\Route instance
	collection									// JetFire\Routing\RouteCollection instance
	middlewareCollection					    // middleware collection
	matcher									    // list of matcher
	dispatcher									// the dispatcher instance
	setConfig($config) 							// router configuration
	getConfig() 								// get router configuration
	run() 										// run the router with the request url 

// JetFire\Routing\Route
$route->
	set($args = []) 							// set your route array parameters
	getUrl() 									// return the route url
	setUrl($url) 								// set the route url
	getName()			 						// return the route name
	setName($name) 								// set the route name
	getCallback()  								// return the route callback (template,controller or anonymous function)
	setCallback($callback) 						// set the route callback
	getMethod() 								// return the route method (GET,POST,PUT,DELETE)
	getDetail() 								// return the route detail 
	setDetail($detail) 							// set the route detail
	addDetail($key,$value)						// add a detail for the route
	getTarget($key = null) 						// return the route target (dispatcher,template or controller|action or function)
	setTarget($target = [])  					// set the route target
	hasTarget($key = null)						// check if the route has the following target
	getData() 								    // return data for the route
	__call($name,$arguments)  					// magic call to get or set route detail

许可证

JetFire 路由是在 MIT 公共许可证下发布的:https://open-source.org.cn/licenses/MIT