php優秀框架codeigniter學習系列——CodeIgniter.php概覽

  CodeIgniter.php是CI框架的核心文件。它在前端控制器index.php以後運行,加載各種基礎組件,執行請求。文件執行完成後,此次請求也就結束了。因此,該文只能對CodeIgniter.php作一個大體的講解,中間若是遇到重要部分,會新寫一篇日誌單獨詳細講解。php

  CI框架的註釋很是的詳細和規範。官方對這個文件的解釋就是 System Initialization File(系統初始化文件),加載基礎類庫和執行請求。它不一樣於index.php只是設置環境和定義重要路徑,而是要深刻框架的核心了。html

  讓咱們一塊兒來學習這個文件吧。前端

1.數組

defined('BASEPATH') OR exit('No direct script access allowed');

第一行代碼,若是沒有定義‘BASEPATH’就退出,而該變量是在index.php文件中定義的。因此用意很明顯,防止該文件被直接訪問。CI框架的不少文件中均可以看到這行代碼。瀏覽器

 

2.  緩存

1 const CI_VERSION = '3.1.7';

定義CI框架的版本,並且是全局變量,每一個框架都有相似的定義吧,爲何不使用define定義而是使用靜態變量呢,不太清楚php框架

 

3.安全

/*
 * ------------------------------------------------------
 *  Load the framework constants
 * ------------------------------------------------------
 */
    if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/constants.php'))
    {
        require_once(APPPATH.'config/'.ENVIRONMENT.'/constants.php');
    }

    if (file_exists(APPPATH.'config/constants.php'))
    {
        require_once(APPPATH.'config/constants.php');
    }

加載框架的常量。若是環境變量(product,development,test)文件夾存在,且文件夾中有constants.php文件,先加載。並且優先級也比後加載的高。constants.php文件定義的是一些不會去作改變的(用define定義)系統級的常量,好比文件可讀寫模式,退出狀態碼等。具體能夠參考constants.php學習app

這裏有一個小技巧,不少框架在定義一個常量前會用defined去檢測是否已定義,對於引用文件則會用file_exists,對於類是否認義則會用class_exists。防止重複定義。composer

 

4.

1 /*
2  * ------------------------------------------------------
3  *  Load the global functions
4  * ------------------------------------------------------
5  */
6     require_once(BASEPATH.'core/Common.php');
View Code

引用經常使用函數文件。這個文件中的函數都是框架中常常用到的函數,也是核心的一些函數。參考common.php學習

 

5.Security procedures

  對於版本小於5.4的php,將全局變量$GLOBALS數組中的一些變量置爲null,說明是出於安全因素。可是我沒有找到這個安全因素的起源。

 

6. 設置自定義錯誤處理程序

1     set_error_handler('_error_handler'); 2     set_exception_handler('_exception_handler'); 3     register_shutdown_function('_shutdown_handler');
View Code

  自定義錯誤處理程序,異常處理程序,php停止執行處理程序。關於CI的錯誤和異常處理機制,請看

 

7.加載config.php

  config.php在application/config/文件夾中定義。定義了一些系統和應用程序的設置,包括使用何種編碼,是否採用csrf,自定義的類的類名前綴等等。在這裏還能夠用在index.php中定義的$assign_to_config數組去覆蓋這些定義的參數。

  

8.use a Composer autoloader

  若是定義了composer_autoload參數,加載這個文件。

 

9.timer

  加載一個計時器。  

 

10.hooks

  hooks容許你在框架運行的特定節點,好比系統運行前,CI_Controller調用前,系統運行結束後等特定的時間節點,執行自定義的函數。官方說法:鉤子特性提供了一種方法來修改框架的內部運做流程,而無需修改 核心文件。我寫了一篇學習文章

 

 11.Important charset-related stuff

  讀取cofig.php中設置的字符集。判斷是否加載了多字節字符串處理的擴展mbstring和字符集轉換的擴展iconv,而且設置這兩個擴展的默認字符集。最後設置php的默認字符集。

 

12.加載兼容性的函數

  因爲php版本的緣由或者沒有開啓某個模塊,一些函數不能使用。在這裏做者對於不能使用的函數,本身進行了補充,放在system/core/compat/文件夾中。在這裏統一加載進框架中。

 

13.加載CI_Utf8類

  這個類主要對Utf8編碼環境提供支持。看來做者是提倡在CI中使用utf8編碼的。參見CI_Utf8類學習

 

14.加載URI類

  這個類主要用來解析uri和決定路由的。關於URI和URL的關係請參考這位朋友的文章。簡單來講URI是惟必定位的資源,URL是惟一資源的一個可能訪問路徑。更多該類的分析,參見CI_URI類學習

 

15.加載Router類

  這個類用來經過URI解析出來 的URI找到訪問的文件。參見CI_Router類學習

  

16.加載Output類

  這個類用主要用來生成返回的頁面給瀏覽器。參見CI_Output類學習。  

 

17.調用緩存

1     if ($EXT->call_hook('cache_override') === FALSE && $OUT->_display_cache($CFG, $URI) === TRUE)
2     {
3         exit;
4     }

  cache_override 使用你本身的方法來替代 輸出類 中的 _display_cache() 方法,這讓你有本身的緩存顯示機制。若是不存在,則調用Output類中的_display_cache()方法,該方法的詳細說明,參見CI_Output類學習

 

18.加載安全類Security

$SEC =& load_class('Security', 'core');

  如註釋所述,該類用來爲防護xss和csrf攻擊提供支持。參見CI_Security類學習

 

18.加載輸入類Input

1 $IN    =& load_class('Input', 'core');

  如註釋所說,加載輸入類,該類用來提早處理全局變量,以保證安全。參見CI_Input類學習

 

18.加載語言類Lang

1 /*
2  * ------------------------------------------------------
3  *  Load the Language class
4  * ------------------------------------------------------
5  */
6     $LANG =& load_class('Lang', 'core');

  如註釋所說,該類提供相關的函數,用於檢索語言文件和文本行,以便國際化。參見CI_Lang類學習

  

19.require_once()應用控制類CI_Controller

 1     // Load the base controller class
 2     require_once BASEPATH.'core/Controller.php';
 3 
 4     /**
 5      * Reference to the CI_Controller method.
 6      *
 7      * Returns current CI instance object
 8      *
 9      * @return CI_Controller
10      */
11     function &get_instance()
12     {
13         return CI_Controller::get_instance();
14     }
15 
16     if (file_exists(APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php'))
17     {
18         require_once APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php';
19     }

CI_Controller是全部應用控制器的父類,因此在這裏須要先導入,以便在下面的路由導入應用控制器的時候,不會由於找不到文件,而報錯。對該文件的具體分析見:CI_Controller分析

 

20. 合理性檢查

/*
 * ------------------------------------------------------
 *  Sanity checks
 * ------------------------------------------------------
 *
 *  The Router class has already validated the request,
 *  leaving us with 3 options here:
 *
 *    1) an empty class name, if we reached the default
 *       controller, but it didn't exist;
 *    2) a query string which doesn't go through a
 *       file_exists() check
 *    3) a regular request for a non-existing page
 *
 *  We handle all of these as a 404 error.
 *
 *  Furthermore, none of the methods in the app controller
 *  or the loader class can be called via the URI, nor can
 *  controller methods that begin with an underscore.
 */

    $e404 = FALSE;
    $class = ucfirst($RTR->class);
    $method = $RTR->method;

    if (empty($class) OR ! file_exists(APPPATH.'controllers/'.$RTR->directory.$class.'.php'))
    {
        $e404 = TRUE;
    }
    else
    {
        require_once(APPPATH.'controllers/'.$RTR->directory.$class.'.php');

        if ( ! class_exists($class, FALSE) OR $method[0] === '_' OR method_exists('CI_Controller', $method))
        {
            $e404 = TRUE;
        }
        elseif (method_exists($class, '_remap'))
        {
            $params = array($method, array_slice($URI->rsegments, 2));
            $method = '_remap';
        }
        elseif ( ! method_exists($class, $method))
        {
            $e404 = TRUE;
        }
        /**
         * DO NOT CHANGE THIS, NOTHING ELSE WORKS!
         *
         * - method_exists() returns true for non-public methods, which passes the previous elseif//對於私有方法同樣會返回true
         * - is_callable() returns false for PHP 4-style constructors, even if there's a __construct()
         * - method_exists($class, '__construct') won't work because CI_Controller::__construct() is inherited
         * - People will only complain if this doesn't work, even though it is documented that it shouldn't.
         *
         * ReflectionMethod::isConstructor() is the ONLY reliable check,
         * knowing which method will be executed as a constructor.
         */
        elseif ( ! is_callable(array($class, $method)))
        {
            $reflection = new ReflectionMethod($class, $method);
            if ( ! $reflection->isPublic() OR $reflection->isConstructor())
            {
                $e404 = TRUE;
            }
        }
    }

    if ($e404)
    {
        if ( ! empty($RTR->routes['404_override']))
        {
            if (sscanf($RTR->routes['404_override'], '%[^/]/%s', $error_class, $error_method) !== 2)
            {
                $error_method = 'index';
            }

            $error_class = ucfirst($error_class);

            if ( ! class_exists($error_class, FALSE))
            {
                if (file_exists(APPPATH.'controllers/'.$RTR->directory.$error_class.'.php'))
                {
                    require_once(APPPATH.'controllers/'.$RTR->directory.$error_class.'.php');
                    $e404 = ! class_exists($error_class, FALSE);
                }
                // Were we in a directory? If so, check for a global override
                elseif ( ! empty($RTR->directory) && file_exists(APPPATH.'controllers/'.$error_class.'.php'))
                {
                    require_once(APPPATH.'controllers/'.$error_class.'.php');
                    if (($e404 = ! class_exists($error_class, FALSE)) === FALSE)
                    {
                        $RTR->directory = '';
                    }
                }
            }
            else
            {
                $e404 = FALSE;
            }
        }

        // Did we reset the $e404 flag? If so, set the rsegments, starting from index 1
        if ( ! $e404)
        {
            $class = $error_class;
            $method = $error_method;

            $URI->rsegments = array(
                1 => $class,
                2 => $method
            );
        }
        else
        {
            show_404($RTR->directory.$class.'/'.$method);
        }
    }

    if ($method !== '_remap')
    {
        $params = array_slice($URI->rsegments, 2);
    }
View Code

 這一段代碼就是判斷到底是否輸出404頁面。固然啦,是否輸出有不少緣由,也就是說,在這裏有不少判斷。

 經過前面加載CI_Router類,已經把要訪問訪問類和方法名都提取出來了。若是訪問的文件找不到,或者類不存在,或者方法不存在,都會設置  $e404 = TRUE  。若是訪問的方法不存在,且存在‘_remap’方法,會將要訪問的方法改爲‘——remap’。

在這裏,還會經過反射的機制,檢測下方法是不是公開可訪問的,不能到時候是私有方法,就尷尬了。反射 ReflectionMethod 的用法可參考[網文]。

- 須要輸出404。若是是自定義了404頁面,會require自定義的404頁面的controller。若是沒有自定義404頁面,或者自定義的404頁面控制器找不到。就調用 show_404  進行默認的404處理。整個請求就完成了。

- 頁面找到了,不須要輸出404頁面。則下一步操做。

 

20. 實例化應用控制類

/*
 * ------------------------------------------------------
 *  Is there a "pre_controller" hook?
 * ------------------------------------------------------
 */
    $EXT->call_hook('pre_controller');

/*
 * ------------------------------------------------------
 *  Instantiate the requested controller
 * ------------------------------------------------------
 */
    // Mark a start point so we can benchmark the controller
    $BM->mark('controller_execution_time_( '.$class.' / '.$method.' )_start');

    $CI = new $class();
View Code

接下來,就是實例化應用控制類啦。 實例化前還有個 hook ,看看你還有什麼提早的操做。 在這一步,會執行控制類中的構造函數__construct(不必定有)。一個典型的應用控制類應該是長這個樣子:

<?php
defined('BASEPATH') OR exit('No direct script access allowed');

class Welcome extends CI_Controller {

    /**
     * Index Page for this controller.
     *
     * Maps to the following URL
     *         http://example.com/index.php/welcome
     *    - or -
     *         http://example.com/index.php/welcome/index
     *    - or -
     * Since this controller is set as the default controller in
     * config/routes.php, it's displayed at http://example.com/
     *
     * So any other public methods not prefixed with an underscore will
     * map to /index.php/welcome/<method_name>
     * @see https://codeigniter.com/user_guide/general/urls.html
     */
    public function index()
    {
        $this->load->view('welcome_message');
    }
}
View Code

 

 21. 調用應用控制類中的方法

/*
 * ------------------------------------------------------
 *  Is there a "post_controller_constructor" hook?
 * ------------------------------------------------------
 */
    $EXT->call_hook('post_controller_constructor');

/*
 * ------------------------------------------------------
 *  Call the requested method
 * ------------------------------------------------------
 */
    call_user_func_array(array(&$CI, $method), $params);

    // Mark a benchmark end point
    $BM->mark('controller_execution_time_( '.$class.' / '.$method.' )_end');
View Code

接下來,是調用應用控制器類的方法了。也就是URI中請求執行的方法。在這以前,會有一個hook, 用戶能夠設置在執行了應用控制器構造函數以後,進行一些操做。

 

22. 收尾

/*
 * ------------------------------------------------------
 *  Is there a "post_controller" hook?
 * ------------------------------------------------------
 */
    $EXT->call_hook('post_controller');

/*
 * ------------------------------------------------------
 *  Send the final rendered output to the browser
 * ------------------------------------------------------
 */
    if ($EXT->call_hook('display_override') === FALSE)
    {
        $OUT->_display();
    }

/*
 * ------------------------------------------------------
 *  Is there a "post_system" hook?
 * ------------------------------------------------------
 */
    $EXT->call_hook('post_system');
View Code

post_controller 是一個hook,能夠定義在整個控制器執行完後的附加處理函數。
display_override 是一個hook,能夠設置自定義的輸出處理函數,若是沒有設置,則執行默認的輸出函數。

 在退出整個執行程序以前,也會執行 hook, post_system 。

 

結尾 

CodeIgniter.php 文件的結束,即標誌着整個 php 程序執行的結束。回顧整個過程,咱們能夠清晰的看到做者在框架中處理整個請求的思路,以下:

 

從圖中能夠看出,寫一個php框架,須要考慮到很是多的方面。 更重要的是,要擴展很多的 方法類庫, 以方便開發者使用和進行各類擴展開發。有時間,我再分析一下煊赫一時的Laravel框架,能夠有一個對比,對框架有更好的理解。

相關文章
相關標籤/搜索