一步一步重写 CodeIgniter 框架 (3) —— 用面向对象重构代码

前面两篇文章为了重点突出 CodeIgniter 框架的原理,程序的结构很乱,有很多全局变量,在这一课中我们采用面向对象的方法对原先代码进行重构。

到目前为止,程序主要完成的就是 URL 分析,并根据配置文件进行路由转换,也就是说我们这里有两个对象,分别处理 URL 分析和路由转换。

1. URL 分析

URL 分析最主要的两个函数是 detect_uri 和 explode_uri ,考虑到 路由的需要,URL 类包含的成员变量有 

segments 数组 原始URL的分段信息

rsegments 数组 经过路由后的分段信息

uri_string URL的路径信息,也就是 index.php 之后的路径

由上述分析及参考上一节的代码:

 1 <?php
 2 
 3 /**
 4  * URI Class
 5  *
 6  * 分析 URI, 并决定路由
 7  */
 8 
 9 class CI_URI {
10 
11     var $segments = array();
12 
13     var $uri_string;
14 
15     var $rsegments;
16 
17     function fetch_uri_string() {
18         if ($uri = $this->detect_uri()) {
19             $this->set_uri_string($uri);
20             return;
21         }
22 
23     }
24 
25     /**
26      * Set 
27      * @param [type] $str [description]
28      */
29     function set_uri_string($str) {
30         $this->uri_string = ($str == '/') ? '' : $str;
31     }
32 
33 
34     /**
35      * --------------------------------------------------------------------
36      * 获取 uri ,并通过 uri 调用相应的方法
37      * --------------------------------------------------------------------
38      */
39 
40     function detect_uri() {
41         
42         if ( ! isset($_SERVER['REQUEST_URI']) OR ! isset($_SERVER['SCRIPT_NAME'])) {
43             return '';
44         }
45 
46         $uri = $_SERVER['REQUEST_URI'];
47         if (strpos($uri, $_SERVER['SCRIPT_NAME']) === 0) {
48             $uri = substr($uri, strlen($_SERVER['SCRIPT_NAME']));
49         }
50 
51         if ($uri == '/' || empty($uri)) {
52             return '/';
53         }
54 
55         $uri = parse_url($uri, PHP_URL_PATH);
56 
57         // 将路径中的 '//' 或 '../' 等进行清理
58         return str_replace(array('//', '../'), '/', trim($uri, '/'));
59     }
60 
61     
62 
63     function explode_uri() {
64 
65         foreach (explode('/', preg_replace("|/*(.+?)/*$|", "\1", $this->uri_string)) as $val) {
66             $val = trim($val);
67             if ($val != '') {
68                 $this->segments[] = $val;
69             }
70         }
71     }
72 
73 }

2. Router 路由类

路由类起的作用就是一个路由转换 set_routing ,并提供给外界获取转换后路由的接口如 fetch_class, fetch_method 等, 根据前面两篇的代码和思路,很容易写出如下的 Router  类。

其中最关键的就是 set_routing 函数,它被入口文件 index.php 执行,通过 URI 类分析获得 分段信息,并根据分析后的结果,获得转换后的路由,设置 class 和 method。

  1 <?php
  2 
  3 class CI_Router {
  4 
  5     var $uri;
  6 
  7     var $routes;
  8 
  9     var $class;
 10 
 11     var $method;
 12 
 13     var $default_controller;
 14 
 15     function __construct() {
 16         global $URI;
 17 
 18         $this->uri = &$URI;
 19     }
 20 
 21     function set_routing() {
 22 
 23         if (is_file('routes.php')) {
 24             include('routes.php');
 25         }
 26 
 27         $this->routes = ( ! isset($route) OR ! is_array($route)) ? array() : $route;
 28         unset($route);
 29 
 30         $this->default_controller = ( ! isset($this->routes['default_controller']) OR $this->routes['default_controller'] == '') ? FALSE : $this->routes['default_controller'];
 31 
 32         $this->uri->fetch_uri_string();
 33 
 34         if ($this->uri->uri_string == '') {
 35             return $this->set_default_controller();
 36         }
 37 
 38         $this->uri->explode_uri();
 39 
 40         $this->parse_routes();
 41     }
 42 
 43 
 44     function set_default_controller() {
 45         
 46     }
 47 
 48     function parse_routes() {
 49         $uri = implode('/', $this->uri->segments);    
 50 
 51         if (isset($this->routes[$uri])) {
 52             $rsegments = explode('/', $this->routes[$uri]);
 53 
 54             return $this->set_request($rsegments);        
 55         }
 56     }
 57 
 58     function set_request($segments = array()) {
 59 
 60         if (count($segments) == 0) {
 61             return $this->set_default_controller();
 62         }
 63 
 64         $this->set_class($segments[0]);
 65 
 66         if (isset($segments[1])) {
 67             $this->set_method($segments[1]);
 68         } else {
 69             $method = 'index';
 70         }
 71 
 72         $this->uri->rsegments = $segments;
 73     }
 74 
 75     function set_class($class) {
 76         $this->class = str_replace(array('/', '.'), '', $class);
 77     }
 78 
 79     /**
 80      * Set the method
 81      * 
 82      * @param string $method the method to execute 
 83      */
 84     function set_method($method) {
 85         $this->method = $method;
 86     }
 87 
 88     /**
 89      * Fetch the class
 90      *
 91      * @return string the class 
 92      */
 93     function fetch_class() {
 94         return $this->class;
 95     }
 96 
 97     /**
 98      * Fetch the method
 99      * 
100      * @return string the method 
101      */
102     function fetch_method() {
103         return $this->method;
104     }
105 }

3. 主入口文件逻辑

使用面向对象重构后,主入口文件只需要创建它们的实例,通过调用它们的方法,即可以处理最重要的工作,然后通过调用 call_user_fun_array 来执行。

 1 <?php
 2 /**
 3  * 框架主入口文件,所有的页面请求均定为到该页面,并根据 url 地址来确定调用合适的方法并显示输出
 4  */
 5 require 'Router.php';
 6 require 'URI.php';
 7 
 8 
 9 $URI = new CI_URI();
10 
11 $RTR = new CI_Router();
12 
13 $RTR->set_routing();
14 
15 
16 $class = $RTR->fetch_class();
17 $method = $RTR->fetch_method();
18 
19 $CI = new $class();
20 
21 call_user_func_array(array(&$CI, $method), array_slice($URI->rsegments, 2));
22 
23 class Welcome {
24 
25     function hello() {
26         echo 'My first Php Framework!';
27     }
28 
29     function saysomething($str) {
30         echo $str.", I'am the php framework you created!";
31     }
32 }

4. 测试

 http://localhost/learn-ci/index.php/welcome/hello ,可以看到与第二课一样的输出结果

hello, I'am the php framework you created!

原文地址:https://www.cnblogs.com/zhenyu-whu/p/3176515.html