国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

文件加載---理解一個project的第一步

2019-11-15 02:03:44
字體:
來源:轉載
供稿:網友
文件加載---理解一個PRoject的第一步

  當我最開始寫php的時候,總是擔心這個問題:我在這兒new的一個class能加載到對應的類文件嗎?畢竟一運行就報Fatal Error,什么**文件沒找到,類無法實例化等等是一種很“低級”的錯誤,怕別人看了笑話。于是每接一個新任務,我總想把它的加載過程弄清楚(以前只知道幾個html標簽和樣式,不知算不算web開發),有時頭兒看了說還有閑心看這個,趕緊寫邏輯,照這樣做就行了......你妹你知道當然有把握了D:,后來發現原來流程都差不多。

  在一個IDE中開發時,如C++/java,一般是新建一個工程,通過IDE新添加一個文件到指定目錄下,然后#include/Import進來即可,php則使這一步驟更加過程化,文件的加載過程基本確定了這個project(框架或者自搭的項目)的目錄結構和文件的分門別類。

   不管框架還是自搭的項目總得有個入口文件,這時要事先加載一些基本信息,如配置文件、通用方法等,使用的基本是手動直接加載單個文件形式,使用下面四個方法之一:

  include、require、include_once、require_once

    include('config.php');    require('database.php');

  涉及到類文件的加載,少部分是直接加載,比如,通用方法作為靜態方法寫在一個類Utilities中,因為是后邊很多要用到的方法(如錯誤輸出、curl請求、隨機字符串生成...),所以用類封裝起來,一般也是在加載配置文件時連帶加載進來

  include('Utilities.php');

  而更通用的情況是:類的動態加載。首先不談的加載的方式,來看看大概什么時候會用到一個類和實例:

  1. 最明顯的,$obj = new A; 它的變種$className = 'A'; $obj = $className; 都一樣;

  2. 類的靜態方法、靜態變量和常量的調用,即Utilities::httpRequest()、Utilities::$instance、Utilities::HOST;

  3. 在php函數中,使用了回調函數的情況,最典型的call_user_func_array()(call_user_func),還有其他用到了callback的地方,如數組中的array_walk、array_map,它們需要一個回調函數作為參數。

  回調函數非常靈活,不止可以是簡單函數,還可以是對象的方法,包括靜態類方法。因為可以用對象方法或靜態方法,所以這鐘時候也要去加載對應的類文件。自php5.3起,回調函數還可以像js中,用匿名函數來實現。

     class A{         public static function cube($var){             return pow($var, 3);         }                  public function twice($var){             return 2*$var;         }     }     // 使用類的靜態方法     $num = call_user_func('A::cube', 5);     // 使用對象     $obj = new A;     $num = call_user_func_array(array($obj, 'twice'), array(7));

  嚴格來說上例中的call_user_func_array在之前已經實例化了對象,但是存在這么個用法,它完全也可以使用類靜態方法。

  首先要明白的是,為什么需要動態加載。php是腳本語言,我們訪問時,是以腳本為可用資源,比如現在根目錄有個index.php文件,它沒有include任何其他文件,當我們直接以localhost/index.php來訪問時,可以訪問到index.php中的全部資源,如果index.php中定義了一個普通類A,在該腳本中實例化一個A的對象時,程序會這樣反應:哦,我已經看到了A的定義,可以直接實例化它(不需要加載其他文件)。如果還有類B、C、D等很多類,全部寫在index.php中顯然不行,那就寫在其他文件中,再include進來(include已經在做加載的工作了),這樣對程序來說,也是“可見”的了。

  但是隨著系統功能的增多,類越來越多,各個類的功能也不同,有的直接定義數據庫的操作,讀取數據庫的數據,有的是控制訪問腳本時要運行的方法,有的則是將要展現出來的頁面,有的是我們引用的第三方核心庫,于是,當我們把所有的文件放在一個目錄中時,雖然可以直接include加載,但這些文件擺放顯得既雜亂無章又難找,維護成本還高。好唄,那就在根目錄下再分別建幾個目錄,目錄A專門存放與數據庫打交道的腳本,目錄B是系統的各種配置信息文件,目錄C是控制我們進入程序時的入口控制方法的腳本,目錄D是即將展示到瀏覽器的頁面......

  于是MVC架構慢慢就演化出來了,我們不能再像以前那樣直接include,腳本都放在特定的目錄下,如Controller目錄下存放的是各種控制器,加載控制器時,我們得這樣include('root/Controller/indexController.php'),每次都在文件前面弄一大串的include不僅看著頭疼,簡直讓人累覺不愛。既然有了獲取當前文件路徑和類名的現成方法,為何不將類名與文件名對應起來,而只要是控制器類的腳本就全放在根目錄的Controller子目錄下邊,就可以寫一個方法,只要是控制器類,在這個方法中運行include(ROOT.'Controller/'.$className.'.php');這一句,ROOT為根目錄常量,$className為傳入的類名,只要是模型類,就這樣include(ROOT.'Model/'.$className.'.php');,全憑這個函數來動態控制到哪個目錄里邊去找,這個project可能就是這樣的:

  無形中,就建立起了類名和文件名的對應規則,文件和所在的目錄的對應規則,該project下有哪些這樣的目錄和文件呢?啊原來是放控制器的Controller、放配置信息的Config等等,再次于無形中得知了這個project的結構,而上面說的,利用函數根據一定條件(傳入參數)可知自動到哪個目錄下去加載該文件,而不是一個個寫死的include,就是所謂的文件的動態加載了。

  因此,當你要新建一個**類文件時,也就知道,哦在這個project中,我應該放在這個目錄下,文件的命名應該與類名相同,這樣就一定能加載到了~~~接下來就是寫業務邏輯的一個“愉快的過程”。

  知道什么時候會動態加載及為什么要動態加載后,接下來就是來實現了,也就是上面說到的利用函數來加載某個文件,就是要寫好這個“函數”來實現這個過程。常用的有三種方式:

  1. __autoload

  我第一次學的時候就是用的就是這個,魔術函數,只要定義了php程序就會在要用到一個類時自動調用它進行文件動態加載,一樣,既然它是個函數,就要讓程序對__autoload的定義可見,不然從哪兒調用它呢?一般來說,作為后邊程序大部分地方要用到的方法,我們都會放在一個單獨的文件中,在程序的入口處加載進來,一個project總得有幾個文件是手動include的,完全可以在開頭單獨include進來,或者放在配置信息中,加載配置信息時就加載進來了。它的原型:

  void__autoload(string $class)

  參數當前加載的類名名稱(注意如果有命名空間,則包含命名空間前綴),下面是一個針對上面的圖片結構的簡單示例:

    // file: autoload.php     // ROOT為已經定義的根目錄常量    function __autoload($className){        try{            if(file_exists(ROOT.'Controller/'.$className.'.php')){// 檢查Controller                include(ROOT.'Controller/'.$className.'.php');            }            else if(file_exists(ROOT.'Model/'.$className.'.php')){// 檢查Model                include(ROOT.'Model/'.$className.'.php');            }            else if(file_exists(ROOT.'Lib/'.$className.'.php')){// 檢查Lib                include(ROOT.'Lib/'.$className.'.php');            }            else{                                               // 找不到該文件                throw new Exception("ERROR: can't find file {$className}.php");            }        }        catch(Exception $e){            echo $e.getMessage();            exit;        }    }

  

  2. spl_autoload_register

  __autoload實際上也差不多了,但它是php定義的,如果現在有個東西寫了并調用之后,就告訴程序說,我不用__autoload來加載文件了,我已經定義了一個專門加載文件的方法(比如名稱是loadClass),以后需要加載一個類文件時,你就用它吧。spl_autoload_register就是這樣一個能告訴程序這樣去做的方法,而且自定義加載方法將會更靈活,可以指定多個加載函數,spl_autoload_register函數會將這些函數放在一個隊列中,并激活它們,在調用時逐個激活:“If there must be multiple autoload functions,spl_autoload_register()allows for this. It effectively creates a queue of autoload functions, and runs through each of them in the order they are defined. ”,php.net上(http://php.net/manual/en/function.spl-autoload-register.php)也確實如此解釋,spl_autoload_unregister則是從加載函數隊列中注銷。

  另外spl_autoload_functions()函數,可以獲取我們注冊了哪些函數;spl_autoload_call($class)函數,嘗試調用所有已注冊的加載函數來加載$class的類文件。

  對于spl_autoload_register的解釋,我的理解是,如果用spl_autoload_register注冊了n個函數在加載隊列中,因為它自動激活它們嘛,現在我要實例化一個類,在第1個加載函數中加載失敗了,然后嘗試第2個函數,第二個失敗則嘗試第3個,''',直到第n個函數走完,若還沒加載成功,就報錯,只要中間一個加載成功就成功了,but事實好像有點出入。

  還是用上一個圖片中的目錄結構,

  1、在Controller目下創建indexController.php文件,包含類indexController;

  2、在Model目錄下創建userModel.php文件,包含類userModel;

  3、首頁寫個類加載腳本Autoload.php,代碼如下:

    // file: Autoload.php    define('DS', DIRECTORY_SEPARATOR);    define('ROOT', rtrim(dirname(__FILE__), '///').DS);        class Autoload{        public static function autoloadRegister($loadFunc = 'Autoload::loadControllerClass', $enable = true){            return $enable ? spl_autoload_register($loadFunc) : spl_autoload_unregister($loadFunc);        }        // 加載控制器類        public static function loadControllerClass($className){            if(file_exists(ROOT.'Controller'.DS.$className.'.php')){// 檢查Controller                include(ROOT.'Controller'.DS.$className.'.php');                echo ROOT.'Controller'.DS.$className.'.php'.'<br/>';            }            else{                echo "ERROR: can't find file {$className}.php in ".ROOT."Controller";                exit;            }        }        // 加載模型類        public static function loadModelClass($className){            if(file_exists(ROOT.'Model'.DS.$className.'.php')){// 檢查Model                include(ROOT.'Model'.DS.$className.'.php');                echo ROOT.'Model'.DS.$className.'.php'.'<br/>';            }            else{                echo "ERROR: can't find file {$className}.php in ".ROOT."Model";                exit;            }        }    }

  4、測試腳本,測試類是否能加載

    // 注冊兩個加載函數    Autoload::autoloadRegister('Autoload::loadControllerClass');    Autoload::autoloadRegister('Autoload::loadModelClass');        // 查看總共注冊了哪些加載函數    echo 'register functions=> <pre>';    print_r(spl_autoload_functions());    // 分別實例化一個Controller類和Model類    $indexCon = new indexController;    $userMod = new userModel;

  結果是這樣

  

  這不科學啊,spl_autoload_functions數組顯示兩個函數都注冊了,但是當實例化userModel類時它還是跑到Controller目錄中去找,兩個類的實例化調用的自動加載方法都是Autoload::loadControllerClass,所以userModel類文件加載報錯......注意到spl_autoload_register方法的第三個參數, 是添加一個加載函數時放在棧中的位置,于是我另寫一個類似的類otherLoad,只是為了將loadModelClass方法放到隊列首部:

    class otherLoad{        public static function autoloadRegister($loadFunc = 'otherLoad::loadModelClass', $enable = true){            // 默認將loadModelClass放在隊首            return $enable ? spl_autoload_register($loadFunc, true, true) : spl_autoload_unregister($loadFunc);        }        // 加載模型類        public static function loadModelClass($className){            if(file_exists(ROOT.'Model'.DS.$className.'.php')){// 檢查Model                include(ROOT.'Model'.DS.$className.'.php');                echo ROOT.'Model'.DS.$className.'.php'.'<br/>';            }            else{                echo "ERROR: can't find file {$className}.php in ".ROOT."Model";                exit;            }        }    } 

  測試是這樣


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 壤塘县| 闵行区| 阜城县| 长寿区| 建湖县| 云安县| 巴中市| 辽阳市| 唐海县| 门源| 郧西县| 通江县| 黔江区| 房山区| 阿拉善左旗| 保亭| 蒙阴县| 岱山县| 新乡市| 清徐县| 抚顺县| 长丰县| 黄浦区| 重庆市| 鄢陵县| 布尔津县| 大城县| 泸溪县| 吉木萨尔县| 大城县| 金乡县| 临泉县| 南阳市| 石嘴山市| 常宁市| 昭觉县| 莎车县| 昌都县| 乌拉特前旗| 田东县| 蒲城县|