一步一步重寫 CodeIgniter 框架 (3) —— 用面向對象重構代碼

 

前面兩篇文章爲了重點突出 CodeIgniter 框架的原理,程序的結構很亂,有不少全局變量,在這一課中咱們採用面向對象的方法對原先代碼進行重構。php

到目前爲止,程序主要完成的就是 URL 分析,並根據配置文件進行路由轉換,也就是說咱們這裏有兩個對象,分別處理 URL 分析和路由轉換。數組

1. URL 分析框架

URL 分析最主要的兩個函數是 detect_uri 和 explode_uri ,考慮到 路由的須要,URL 類包含的成員變量有 函數

segments 數組 原始URL的分段信息測試

rsegments 數組 通過路由後的分段信息fetch

uri_string URL的路徑信息,也就是 index.php 以後的路徑ui

由上述分析及參考上一節的代碼:this

複製代碼

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 路由類url

路由類起的做用就是一個路由轉換 set_routing ,並提供給外界獲取轉換後路由的接口如 fetch_class, fetch_method 等, 根據前面兩篇的代碼和思路,很容易寫出以下的 Router  類。spa

其中最關鍵的就是 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!

相關文章
相關標籤/搜索