在框架中配置文件多目录、前后端应该是很常见的事情。像一般的php框架(CI、Tp等)都采用单入口模式,可能有人会直接在框架根目录新建一个文件admin.php,然后改变框架app的结构来达到效果访问不同的入口文件名以获得不同的资源。那么在CI中也可以这样做,但是个人感觉这种方式太浪费资源了(占用了几十k的资源)。于是在“边学边问”的路上,终于得到了一个比较完美的解决方案。业务需求环境:codeigniter3需求:在CI3中实现前后端效果。示例:地址栏输入xxx.com默认访问前端首页,输入xxx.com/admin访问后台。按照惯例,我们会在框架中的config/route.php路由配置文件中配置我们的前后台访问路径://path=>application/config/route.php$route['admin']='管理员/管理员';//后台路径$route['default_controller']='home/home';//默认前景路径$route['404_override']='';$route['translate_uri_dashes']=FALSE;一般来说我们这样配置是没有问题的,但是有一个条件是CI3以下的版本是没有问题的。但是现在框架版本是CI3,所以会出现找不到资源文件的情况,空接口无效。下面是CI2和CI3的两个路由配置图,浏览器效果图:CI2和CI3有相同的路由配置对比图:CI2和CI3有相同的路由配置运行网页对比图:从上面两个图可以看出,在相同的路由配置下,结果是不同的。因为CI3不再支持设置子目录下控制器为默认控制器的功能。但是要完成需求描述,如何实现这样的效果呢?接下来看看我们的CI3源码追踪;源码跟踪core/Route.php通过上面的结论,我们应该能想到404错误,应该是解析default_controller时的问题,所以我在sublime中使用全文搜索查询default_controller有用的地方,搜索范围可以假设在CI的core目录下,因为路由分析一般都是由core目录下的路由类完成的,所以查询范围锁定在system目录下,得到如下结果:lock_set_default_controller方法,于是一步步排查,终于发现了这段代码的问题://是否指定了方法?如果(sscanf($this->default_controller,'%[^/]/%s',$class,$method)!==2){$method='index';}if(!file_exists(APPPATH.'controllers/'.$this->directory.ucfirst($class).'.php')){//这样会在后面触发404return;}在上面代码的第一段中,拆分我们在config/中配置的default_controller值route.php获取控制器名称并将其分配给变量类和方法名称并将其分配给方法。如果method为空,则默认为index,这显然不符合我们的初衷,因为我们的规划是default_controller中的值home/home,第一个home是目录名(floder_name),第二个home是控制器名(controller_name),第二个if表示判断控制器文件是否存在,查了也发现控制器名不存在,打印APPPATH。'控制器/'。$这个->目录。ucfirst($类)。'.php'得到:E:\WWW\ci3\application\controllers/Home.php,这显然和我们的实际目录不符,我们的实际目录应该是E:\WWW\ci3\apapplication\controllers/home/Home.php,锁定了这两个问题之后,就可以考虑怎么修复这个地方了。一开始改这个地方的思路是:假设默认controller值设置为Home/home/index修改core/Route.php源码中的_set_default_controller方法,形式为(目录name/controllername/methodname),拦截default_controller的值进行处理,修改_set_default_controller方法如下:显示。路由文件中未指定默认路由。');}/***if中自己修改的部分*1.截取default_controller为Array*2.如果default_controller_arr大于3,说明默认controller过来*3.赋值对应的变量*/$default_controller_arr=explode('/',$this->default_controller);if(count($default_controller_arr)==3){//分配控制器目录$this->directory=trim($default_controller_arr[0],'/').'/';//分配控制器名称$class=$default_controller_arr[1];//因为这里plan约定默认controller输入的是完整的uri,即目录名/控制器名/方法名的形式//所以这里方法名不能为空$method=$default_controller_到达[2];}else{//是否指定了方法?如果(sscanf($this->default_controller,'%[^/]/%s',$class,$method)!==2){$method='index';}}if(!file_exists(APPPATH.'controllers/'.$this->directory.ucfirst($class).'.php')){//这将在稍后触发404return;}$this->set_class($class);$this->set_method($method);//分配路由段,索引从1开始$this->uri->rsegments=array(1=>$class,2=>$method);log_message('debug','NoURIpresent.Defaultcontrollerset.');}虽然修改后测试成功,但并不是最好的方案(修改源码一般是最后的方案),所以求助于codeigniter中国官方微信群里的小伙伴们,在群里和Hex的大佬(手动@Hex)讨论了这个功能的解决方案,最后在他的帮助下得到了一个完美的解决方案,就是在application/在core中创建自己的扩展路由类MY_Router.php,然后定义自己的_set_default_controller方法,代码如下,顺便贴上上面想象的解决方案:default_controller)){show_error('无法确定应该显示什么。默认路由没有已在路由文件中指定。');}if(sscanf($this->default_controller,'%[^/]/%s',$class,$method)!==2){$method='index';}/***1.判断目录是否存在*2.如果存在则调用设置controller目录的方法详见system/core/Route.phpset_directory方法*3.然后拆分方法和赋值给$class$method$methodis如果为空,则设置为index*/if(is_dir(APPPATH.'controllers/'.$class)){//设置类为目录$this->set_directory($类);//$method是类$class=$method;//如果设置了方法,则重新检查斜杠if(sscanf($method,'%[^/]/%s',$class,$method)!==2){$method='索引';}}if(!file_exists(APPPATH.'controllers/'.$this->directory.ucfirst($class).'.php')){//这将在稍后触发404return;}$this->set_class($class);$this->set_method($method);//分配路由段,索引从1开始$this->uri->rsegments=array(1=>$class,2=>$method);log_message('debug','NoURIpresent.Defaultcontrollerset.');}/***@author打水,*@date(2017-8-7)**使用this方法时,将方法名替换成上面的方法名*application/config/route.phpdefault_controller的值可以写uri的全名(目录名/控制器名/方法名)*因为最后Route.php路由库调用或者_set_default_controller方法*/protectedfunction_set_default_controller_me(){if(empty($this->default_controller)){show_error('无法确定应该显示什么。路由文件中未指定默认路由。');}/***if中自己修改的部分*1.将default_controller截取为数组*2.如果default_controller_arr大于3,说明默认controller来自*3.赋值对应的变量*/$default_controller_arr=explode('/',$this->default_controller);if(count($default_controller_arr)==3){//分配控制器目录$this->directory=trim($default_controller_arr[0],'/').'/';//分配控制器名称$class=$default_controller_arr[1];//因为plan约定默认控制器输入完整的uri,即目录名/控制器名/方法名的形式//所以这里的方法名一定不能为空$method=$default_controller_arr[2];}else{if(sscanf($this->default_controller,'%[^/]/%s',$class,$method)!==2){$method='index';}}if(!file_exists(APPPATH.'controllers/'.$this->directory.ucfirst($class).'.php')){//这将触发404稍后返回;}$this->set_class($class);$this->set_method($method);//分配路由段,索引从1开始$this->uri->rsegments=array(1=>$class,2=>$method);log_message('debug','NoURIpresent.Defaultcontrollerset.');}}上面的代码比较完美,亲测有效!!!(自己简单测试一下,也可以用)资源参考文章如何选择子文件夹中的默认控制器?如何在CodeIgniter3资源MY_Route.phpCodeIgniter中国微信群中使用默认控制器路由中的子文件夹
