php开源一个mvc框架的诞生之路由实现

Posted OpenFramework

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了php开源一个mvc框架的诞生之路由实现相关的知识,希望对你有一定的参考价值。

一个有效的浏览器请求是:


用户电脑端打开浏览器—>输入网址请求—>服务端接受浏览器请求—>然后处理好逻辑返回给浏览器—>浏览器根据载入的资源渲染在窗口上给用户展示。


开发路由需要制定一系列规则,以便更好的响应请求。


class Router
{

   /**
    * @param array $rule
    * 路由规则
    */
   private $rule = [];

   /**
    * @param string $url_module
    * url模式
    * URL模式 0普通模式 1 pathinfo模式 2 rewrite模式
    */
   private $url_module = '1';

   /**
    * @param array $url
    * 封装后请求地址
    */
   private $url = [];

   /**
    * @param array $path
    * 请求地址
    */
   private $path;

   /**
    * @param string $default_platform
    * 默认操作平台
    */
   private $default_platform = 'main';

   /**
    * @param string $default_controller
    * 默认控制器
    */
   private $default_controller = 'Home';

   /**
    * @param string $default_action
    * 默认操作方法
    */
   private $default_action = 'main';

   /**
    * @param string $var_platform
    * 默认模块传参变量
    */
   private $var_platform = 'm';

   /**
    * @param string $var_controller
    * 默认控制器传参变量
    */
   private $var_controller = 'c';

   /**
    * @param string $var_action
    * 默认方法传参变量
    */
   private $var_action = 'a';

   /**
    * @param bool $route_rules_on
    * 是否开启路由自定义配置
    */
   private $route_rules_on = true;

   /**
    * @param string $get_param
    * get参数
    */
   private $get_param;

   private $platform;

   private $controller;

   private $action;

   private $dir = APPLICATION_PATH;

   private $namespace = APP_NAMESPACE_NAME;

   public function run(Router $router){}

   public function import($rules)
   {
       if(is_array($rules)){
           foreach($rules as $tag => $rule){
               $this->addRule($rule);
           }
       }
       return $this;
   }

   public function addRule($rule)
   {
       if($rule instanceof Closure){
           $ruleClosure[] = $rule;
           $this->rule = array_merge($this->rule,$ruleClosure);
       }
       $this->rule = array_merge($this->rule,$rule);
       return $this;
   }

   public function parser()
   {
       (new Parser())->parserPath($this);
       return $this;
   }

   public function dispatch()
   {
       (new Dispatch())->dispatch($this);
       return $this;
   }

   public function setUrlModel($model)
   {
       $this->url_module = $model;
       return $this;
   }

   public function setUrl($url)
   {
       $this->url = $url;
       return $this;
   }

   public function setPath($path)
   {
       $this->path = $path;
       return $this;
   }

   public function setDefaultPlatform($platform)
   {
       $this->default_platform = $platform;
       return $this;
   }

   public function setDefaultController($controller)
   {
       $this->default_controller = $controller;
       return $this;
   }

   public function setDefaultAction($action)
   {
       $this->default_action = $action;
       return $this;
   }

   public function setVarPlatform($platform)
   {
       $this->var_platform = $platform;
       return $this;
   }

   public function setVarController($controller)
   {
       $this->var_controller = $controller;
       return $this;
   }

   public function setVarAction($action)
   {
       $this->var_action = $action;
       return $this;
   }

   public function setRouterOn($bool)
   {
       $this->route_rules_on = $bool;
       return $this;
   }

   public function setGetParam($param)
   {
       $this->get_param = $param;
       return $this;
   }

   public function setPlatform($platform)
   {
       $this->platform = $platform;
       return $this;
   }

   public function setController($controller)
   {
       $this->controller = $controller;
       return $this;
   }

   public function setAction($action)
   {
       $this->action = $action;
       return $this;
   }

   public function setDir($dir)
   {
       $this->dir = $dir;
       return $this;
   }

   public function setNamespace($namespace)
   {
       $this->namespace = $namespace;
       return $this;
   }


   /////////////////参数获取//////////////////////

   public function getUrlModel()
   {
       return $this->url_module;
   }

   public function getUrl($key='')
   {
       return $key == '' ? $this->url : $this->url[$key];
   }

   public function getPath()
   {
       return $this->path;
   }

   public function getDefaultPlatform()
   {
       return $this->default_platform;
   }

   public function getDefaultController()
   {
       return $this->default_controller;
   }

   public function getDefaultAction()
   {
       return $this->default_action;
   }

   public function getVarPlatform()
   {
       return $this->var_platform;
   }

   public function getVarController()
   {
       return $this->var_controller;
   }

   public function getVarAction()
   {
       return $this->var_action;
   }

   public function getRouterOn()
   {
       return $this->route_rules_on;
   }

   public function getRule()
   {
       return $this->rule;
   }

   public function getGetParam()
   {
       return $this->get_param;
   }

   public function getPlatform()
   {
       return $this->platform;
   }

   public function getController()
   {
       return $this->controller;
   }

   public function getAction()
   {
       return $this->action;
   }

   public function getDir()
   {
       return $this->dir;
   }

   public function getNamespace()
   {
       return $this->namespace;
   }

}


初始化路由类通过import方法导入路由规则配置文件,然后设置相关路由配置,最后执行路由解析器

parser

方法


最后执行路由分发

dispatch

方法


使用方法案例:


Application::router()
   ->import(LOAD_PATH . 'router.php')
   ->setUrlModel('1')
   ->setPath('')
   ->setDefaultPlatform('main')
   ->setDefaultController('Home')
   ->setDefaultAction('main')
   ->setVarPlatform('m')
   ->setVarController('c')
   ->setVarAction('a')
   ->setRouterOn('true')
   ->setGetParam(Application::input('get.'))
   ->setPlatform('')
   ->setController('')
   ->setAction('')
   ->setDir(APPLICATION_PATH)
   ->setNamespace(APP_NAMESPACE_NAME)
   ->parser()
   ->dispatch()

完整的路由设置启动分发,具体的方法router类有注释


class Parser
{

   private $_router;

   public function parserPath(Router $router)
   {
       $this->_router = $router;
       $path = '';
       /**
        * 检测URL模式以及是否开启自定义路由配置
        */
       if($this->_router->getUrl() != '0' && $this->_router->getRouterOn()){
           if(array_key_exists($this->_router->getPath(),$this->_router->getRule())){
               $rule = $this->_router->getRule();
               $path = $rule[$path];
           }
       }
       /**
        * URL参数匹配
        */
       $this->parserParam($path);
   }

   public function parserParam($path)
   {
       $url = preg_replace('/\.html$/','',$path);
       switch($this->_router->getUrlModel()){
           case 0:
               $this->initDispatchParamByNormal();
               break;
           case 1:
               $dispatch = explode('/',trim($url,'/'));
               if(in_array('index.php',$dispatch)){
                   $param['platform'] = isset($dispatch['1']) ? $dispatch['1'] : '';
                   $param['controller'] = isset($dispatch['2']) ? $dispatch['2'] : '';
                   $param['action'] = isset($dispatch['3']) ? $dispatch['3'] : '';
                   $this->getValue($url,4);
               } else {
                   $param['platform'] = isset($dispatch['0']) ? $dispatch['0'] : '';
                   $param['controller'] = isset($dispatch['1']) ? $dispatch['1'] : '';
                   $param['action'] = isset($dispatch['2']) ? $dispatch['2'] : '';
                   $this->getValue($url,3);
               }
               $this->_router->setUrl($param);
               $this->initDispatchParamByPathInfo();
               break;
           case 2:
               $this->initDispatchParamByNormal();
               break;
       }
   }

   private function getValue($url,$start)
   {
       $get = explode('/',trim($url,'/'));
       if(count($get)>3){
           $param = array_slice($get,$start);
           for($i=0;$i<count($param);$i+=2){
               $_GET[$param[$i]] = $param[$i+1];
           }
           return $_GET;
       }
   }

   /**
    * 默认模式下初拼接分发参数
    */
   private function initDispatchParamByNormal(){
       //定义常量保存操作平台
       define('PLATFORM',isset($_GET[$config['var_platform']]) ? strtolower($_GET[$config['var_platform']]) : static::$_default_platform);
       //定义常量保存控制器
       define('CONTROLLER',isset($_GET[$config['var_controller']]) ? ucfirst($_GET[$config['var_controller']]) : static::$_default_controller);
       //定义常量保存操作方法
       define('ACTION',isset($_GET[$config['var_action']]) ? strtolower($_GET[$config['var_action']]) : static::$_default_action);
   }

   /**
    * pathinfo 模式下拼接分发参数
    */
   private function initDispatchParamByPathInfo(){
       //定义常量保存操作平台
       $this->_router->setPlatform(
           $this->_router->getUrl('platform') == ''
               ?
               $this->_router->getDefaultPlatform()
               :
               strtolower($this->_router->getUrl('platform'))
       );
       //定义常量保存控制器
       $this->_router->setController(
           $this->_router->getUrl('controller') == ''
               ?
               $this->_router->getDefaultController()
               :
               strtolower($this->_router->getUrl('controller'))
       );
       //定义常量保存操作方法
       $this->_router->setAction(
           $this->_router->getUrl('action') == ''
               ?
               $this->_router->getDefaultAction()
               :
               strtolower($this->_router->getUrl('action'))
       );
   }

}


路由解析类,解析路由根据自定的规则找出该次请求的模块,控制器以及方法然后分发都各自的模块控制器方法中,如果没有即使用默认的模块控制器以及方法进行填补,我设置的默认模块是main。控制器是Home以及方法为main,如果解析后的URL没有相关配置会通过默认的配置进行分发。


将URL中分析出来的get参数进行设置到$_GET参数中获取自定义到其他类中,后续我将全部设置到请求类中,方便后期过滤。


class Dispatch
{

   //分发方法
   static public function dispatch(Router $router)
   {
       $dir = $router->getDir() . 'controller/' . $router->getPlatform();
       //判断模块是否存在
       if(!is_dir($dir)){
           //抛出异常
           throw new \Exception("无法加载模块");
       }
       //实例化控制器类
       $controller_name = $router->getNamespace()  . '\controller\\' . $router->getPlatform() . '\\' . $router->getController();
       $filename = str_replace('\\','/',$dir . '/' . $router->getController()  . '.php');
       if(file_exists($filename)){
           Application::bind(Application::definition()
               ->setAlias($controller_name)
               ->setIsSingleton(true)
               ->setClassName($controller_name));
       } else {
           //抛出异常
           throw new \Exception("无法加载控制器");
       }
       //调用方法
       $action_name = $router->getAction();
       $controller = Application::get($controller_name);
       if(method_exists($controller,$action_name)){
           Application::run()->setData($controller->$action_name());
       } else {
           //抛出异常
           throw new \Exception("无法加载方法");
       }
   }
}


分发类,将解析器中分析出来的模块控制器以及 方法设置到router类中,将router类注入到分发类中即可获取到请求的模块控制器方法,昨天说过要使用依赖注入根据浏览器请求自动注入到控制器中,这里便用到了依赖注入,补全控制器类名然后通过bind方法将控制器注入进行,然后通过get方法获取该控制器,经过依赖注入内部会进行分析实例的控制器内是否需要注入其他类,返回该控制器的实例,然后根据请求的方法进行调用控制器内的方法,这里将方法返回的信息都设置到request类中,有request中进行返回,所以这里建议的是,尽量不要在方法中使用echo进行输入还是使用标准的return返回数据,有request进行输出,如果由request接管输入输出对api开发更为友好,输出的格式可以通过配置进行改变,目前支持原始数据输出,json以及xml格式数据输出。

以上是关于php开源一个mvc框架的诞生之路由实现的主要内容,如果未能解决你的问题,请参考以下文章

php开源一个mvc框架的诞生之Application类input方法

原创Zend Framework 2框架之MVC

原创Zend Framework 2框架之MVC

[翻译]为MVC框架构建路由

PHP原生实现简易的MVC框架

PHP趣味程序 - 利用 Composer 一步一步构建自己的 PHP 框架之设计 MVC