路由分组:可以把相同前缀的路由合并成一个分组,好处就是提高路由匹配的效率(手册上说的,原因等后面补吧,现在也不知道为啥)。例如同一条路由根据参数和访问方式的变化可以访问不同的控制器和方法(每条单独设置也可以达到这个效果,但我感觉这样设置更整齐一些,更何况还说可以提高效率)。比如访问url:module/controller/action/name/Bob,通过设置路由分组之后,post和get可以访问到不同方法。或者更改传参module/controller/action/name/123(之前是字符串,现在是数字)。
大致分支:
1 function group($name, $routes, $option = [], $pattern = []) 2 { 3 if (! empty($name)) { 4 if ($routes instanceof \Closure) { 5 //转下文中 2.name加闭包 6 } else { 7 //.....转下文中 1.简单案例 8 } 9 } elseif ($routes instanceof \Closure) { 10 //转下文中 3.只闭包 11 } else { 12 //转下文中 4.其他 13 } 14 }
1.简单案例:例如下面两个路由可以合并成一个分组
1 'blog/:id' => ['Blog/read', ['method' => 'get'], ['id' => '\d+']], 2 'blog/:name' => ['Blog/read', ['method' => 'post']], 3 4 5 //合并 6 '[blog]' => [ 7 ':id' => ['Blog/read', ['method' => 'get'], ['id' => '\d+']], 8 ':name' => ['Blog/read', ['method' => 'post']], 9 ] 10 11 //方法执行 12 Route::group('blog',[ 13 ':id' => ['Blog/read', ['method' => 'get'], ['id' => '\d+']], 14 ':name' => ['Blog/read', ['method' => 'post']], 15 ]);
执行方法:public static function group($name, $routes, $option = [], $pattern = [])
执行过程:先以上文中的为例,别的情况再补充
(1)遍历第二个参数,这个数组的每条记录的之前设置路由参数几乎是一致的,键值是rule(:id和:name,省略了blog,这里只是记录了参数),数组内容分别是路由,路由参数,和变量规则('Blog/read', ['method' => 'get'], ['id' => '\d+'])
(2)判断是否为完全匹配,根据键值后面是否有$符号(:id和:name),和配置文件的参数route_complete_match,前面的优先级高。去除键值后面的'/'(为了兼容性)。
(3)获取参数,获取路由后缀,这个和之前提到的一样
(4)执行第一篇的第5步存储路由。
(5)将上面的数组存在*中,以name为键值,存储的内容大概如下(上面记录的这些感觉都跟单独设置路由的执行过程差不多,但这一步有些差异)
1 [*] => Array 2 ( 3 [blog] => Array 4 ( 5 [rule] => Array //这里存储了:id和:name的信息,规则和之前一致 6 ( 7 [0] => Array 8 ( 9 [rule] => :id 10 [route] => Blog/read 11 [var] => Array 12 ( 13 [id] => 1 14 ) 15 16 [option] => Array 17 ( 18 [method] => get 19 ) 20 21 [pattern] => Array 22 ( 23 [id] => \d+ 24 ) 25 26 ) 27 28 [1] => Array 29 ( 30 [rule] => :name 31 [route] => Blog/read 32 [var] => Array 33 ( 34 [name] => 1 35 ) 36 37 [option] => Array 38 ( 39 [method] => post 40 ) 41 42 [pattern] => Array 43 ( 44 ) 45 46 ) 47 ) 48 49 [route] => //这里在逻辑中就置为空 50 [var] => Array //这里在逻辑中就置为空 51 ( 52 ) 53 54 [option] => Array //这是传的参数$option 55 ( 56 ) 57 58 [pattern] => Array //这里是传的参数$pattern 59 ( 60 ) 61 62 ) 63 64 )
(6)和上一篇的第8步一致。
总结:大致过程和存储方式和之前的都几乎一致,除了存在*中的格式有所变化。
2:name加闭包:这里的执行过程大概是第一篇的复杂化(并没有复杂太多,这里正好补充第一篇中关于分组的内容,Route::any实际上走的也是Route::rule,只是把type设为*,下面的例子是直接在tp文档中摘的),例如执行下面这个语句:
1 Route::group('blog',function(){ 2 Route::any(':id','blog/read',['method'=>'get'],['id'=>'\d+']); //注意:这里传的rule并非是完整的,只是传了参数(:id,写成/:id也可以,/在逻辑中会被去除,下面有说)。如果设置了分组,则组内所用成员公用一个rule 3 Route::any(':name','blog/read',['ext'=>'html', 'method' => 'post'],['name'=>'\w+']); 4 },['method'=>'get','ext'=>'html'], ['id'=>'\w+']);
(1)这里先说明下另一个静态变量$group(array),记录关于分组的信息,大致张下面这样,记录了三个值分别name,路由参数和变量规则。可以对照上面的执行例子,这三个值分别对应上面传参的第1个,第3个和第4个参数。这也是执行的第(1)步,就是将这三个参数赋值给$group。
1 Array 2 ( 3 [name] => blog 4 [option] => Array 5 ( 6 [method] => get 7 [ext] => html 8 ) 9 10 [pattern] => Array 11 ( 12 [id] => \w+ 13 ) 14 )
(2)执行匿名函数,例子中匿名函数注册了两条路由,实际执行过程参照上一篇,但有一些差异如下(补充第一篇分组):
①这个执行的位置在第一篇中第1步之前。在执行第一条路由(Route::any(':id','blog/read',['method'=>'get'],['id'=>'\d+']))的时候,先要把传入的option和pattern和上文中group中的option和pattern合并(merge)一下。看加闭包的那个例子,也就是说闭包(第二个参数)里面设置的路由的参数都要和外层的第三个和第四个merge一下(乱。。。),也就是说这个分组如果有相同的规则,统一写在外层就行了,把自己特有的规则写在闭包的路由设置中。
②这个执行的位置在第一篇中第3步中。如果判断是分组也会去掉rule两侧的‘/’。
③这个执行的位置在第一篇第5步中。如果是分组键值则使用分组名+route(blog/:id,这个效果和之前一样,只是因为现在传的rule只是:id,所以要拼接一下)。
④这个执行的位置在第一篇第7步中。如果判断是分组在*中的键值设置为组名,格式跟上面提到的一样(以前是这样的new/:id,现在是组名)。
(3)补充外层的一些参数:
1 self::$rules['*'][$name]['route'] = ''; 2 self::$rules['*'][$name]['var'] = self::parseVar($name); //根据分组名称取参数,例子中的分组名称是没有参数的,如果传blog/:test会有一个参数 3 self::$rules['*'][$name]['option'] = $option; //这个参数是上面调用group函数时传的 4 self::$rules['*'][$name]['pattern'] = $pattern; //这个也是上面调用group函数时传的
(4)和上一篇的第8步一致。
3:只闭包 作用:正常情况下的分组的作用是路由分组+公共参数规则+公共路由参数,只闭包的作用是公共路由参数。 例子:
Route::group(['method'=>'get','ext'=>'html'],function(){ //下面两条路由的公用规则 Route::any('blog/:id','blog/read',[],['id'=>'\d+']); Route::any('blog/:name','blog/read',[],['name'=>'\w+']); });
这个是传了name的,但因为下面这段代码被判断为没有name
if (is_array($name)) { $option = $name; //这里把参数option也重新赋值了 $name = isset($option['name']) ? $option['name'] : ''; //这里没有name下面的逻辑会判断name为空 }
后面的执行过程就和分支2一样了,但少了分支2的(3)(4)步。
补充:这种方式并没有进行分组,只是把外部的参数都加到了里面。也就是说个效果是一样的。应用场景应该是当需要注册大量有相同路由规则的路由的时候,可以把这些相同的路由规则拎出来。
Route::any('blog/:id','blog/read',['method'=>'get','ext'=>'html'],['id'=>'\d+']);
Route::any('blog/:name','blog/read',['method'=>'get','ext'=>'html'],['name'=>'\w+']);
看一下数组结构:
1 [*] => Array 2 ( 3 [blog/:id] => Array 4 ( 5 [rule] => blog/:id 6 [route] => blog/read 7 [var] => Array 8 ( 9 [id] => 1 10 ) 11 12 [option] => Array 13 ( 14 [method] => get 15 [ext] => html 16 ) 17 18 [pattern] => Array 19 ( 20 [id] => \d+ 21 ) 22 23 ) 24 [blog/:name] => Array 25 ( 26 [rule] => blog/:name 27 [route] => blog/read 28 [var] => Array 29 ( 30 [name] => 1 31 ) 32 [option] => Array 33 ( 34 [method] => get 35 [ext] => html 36 ) 37 [pattern] => Array 38 ( 39 [name] => \w+ 40 ) 41 ) 42 )
4.和第3种方式一样,只是用数组代替闭包。下面的例子和上面的执行后效果一样
1 Route::group(['method'=>'get','ext'=>'html'], 2 [ 3 ['blog/:id', 'blog/read', [], ['id'=>'\d+']], 4 ['blog/:name','blog/read',[],['name'=>'\w+']] 5 ]);
这里其实是在self::rule中才体现效果,大致逻辑如下:
1 <?php 2 3 public static function group($name, $routes, $option = [], $pattern = []) 4 { 5 self::rule($routes, '', '*', $option, $pattern); 6 } 7 8 public static function rule($rule, $route = '', $type = '*', $option = [], $pattern = []) 9 { 10 if (is_array($rule) && empty($route)) { 11 foreach ($rule as $key => $val) { 12 if (is_numeric($key)) { 13 $key = array_shift($val); //这里把数组中的rule赋值给key,并移除rule 14 } 15 if (is_array($val)) { 16 $route = $val[0]; //此时val的第一个元素变成了route(上一步移除了rule) 17 $option1 = array_merge($option, $val[1]); //这里就是赋值$option和$pattern了, merge是因为要拿外层的公共参数 18 $pattern1 = array_merge($pattern, isset($val[2]) ? $val[2] : []); 19 } else { 20 // 当key值放rule的时候,会执行这里。比如执行这个 21 // Route::group(['method'=>'get','ext'=>'html'], 22 // [ 23 // 'blog/:id' => ['blog/read', [], ['id'=>'\d+']], 24 // 'blog/:name' => ['blog/read',[],['name'=>'\w+']], 25 // ]); 26 } 27 $option1 = null; 28 $pattern1 = null; 29 $route = $val; 30 } 31 self::setRule($key, $route, $type, !is_null($option1) ? $option1 : $option, !is_null($pattern1) ? $pattern1 : $pattern, $group); 32 } 33 34 }