YiluPHP是如何做到不用配置、不用注入就能直接使用所有的類?

使用過YiluPHP的人都會發現,不管是模型類還是邏輯類、輔助類還是工具類,使用所有類都不需要在配置文件中設置加載或注入,也不需要在頁面中使用 include 或 require 或 use ,直接使用   $app->類名->方法名()   就可以了,這個機制如此方便,剛開始使用的人都會有點不知得措,擔心自己是不是做錯了什麼?我現在告訴你,你沒有少做啥,也沒有做錯啥,YiluPHP就是這樣設計的,下面我來告訴你YiluPHP是如何做到的。

有人可能會想使用 spl_autoload_register() 函數就能做到, spl_autoload_register() 函數可以註冊任意數量的自動加載器,比如第三方庫中的,要找一個類需要遍歷所有的自動加載器,效率很低,這不符合YiluPHP追求速度的原則。YiluPHP的 $app 是一個全局變量,是名爲YiluPHP的類的實例,這個類使用了php的魔術方法 __get,代碼如下:

    public function __get($name)
    {
        if (isset($this->helper[$name])) {
            return $this->helper[$name];
        }
        $fun = $this->autoload_class;
        $class_name = $fun($name);
        unset($fun);
        if ($class_name!==false){
            $this->helper[$name] = new $class_name;
            return $this->helper[$name];
        }
        throw new Exception($this->lang('class_not_found').$name);
    }

當使用   $app->類名->方法名()   時,會先從$app的helper屬性中查找是否已經有對應的類實例(helper屬性是一個容器,裝有所有已經使用過的類實例,所以當同一個類被第二次使用時不會再去查找文件,也不會再做實例化操作,直接從helper容器中返回,helper容器的設計也是YiluPHP運行迅速的原因之一),若在容器找不到對應的類實例,會調用自身的 autoload_class() 函數查找文件,autoload_class() 函數是賦值給$app的一個屬性的,它的實現在$app的初始化函數 __construct() 中,

    public function __construct()
    {
        $this->autoload_class = function ($class_name){
            $file = $GLOBALS['project_root'].'helper/'.$class_name.'.php';
            if (file_exists($file)) {
                //helper類文件的文件名、類名、app中的調用方法三者需要一致
                require_once($file);
                return $class_name;
            }

            //將駝峯式的名稱用下劃線分割
            $path = preg_replace('/(?<=[a-z])([A-Z])/', '_$1', $class_name);
            $path = explode('_', $path, 2);
            $path = $path[0].'/'.$class_name;
            $file = $GLOBALS['project_root'].$path.'.php';
            if (file_exists($file)) {
                //類文件的文件名、類名、app中的調用方法三者需要一致
                require_once($file);
                return $class_name;
            }

            //支持給類取別名
            if(!empty($GLOBALS['config']['helper_alias']) && array_key_exists($class_name, $GLOBALS['config']['helper_alias']) ){
                $real_class_name = $GLOBALS['config']['helper_alias'][$class_name];
                $file = $GLOBALS['project_root'].'helper/'.$real_class_name.'.php';
                if (file_exists($file)) {
                    require_once($file);
                    return $real_class_name;
                }

                //將駝峯式的名稱用下劃線分割
                $path = preg_replace('/(?<=[a-z])([A-Z])/', '_$1', $real_class_name);
                $path = explode('_', $path, 2);
                $path = $path[0].'/'.$real_class_name;
                $file = $GLOBALS['project_root'].$path.'.php';
                if (file_exists($file)) {
                    require_once($file);
                    return $real_class_name;
                }
            }
            return false;
        };
    }

autoload_class() 函數先去helper目錄中查找類文件,若找不到,再根據類名前綴單詞去相應的目錄查找類文件(這中間還省略了根據類的別名查找的流程),再找不到就拋出『找不到類』的異常。其實最早作者打算把所有的類文件都存放在helper目錄中的,這是爲了減少程序執行流程,從而提升框架的運行速度。但是考慮以下兩個方面的原因,還是決定支持劃分目錄存放類:

第一,如果系統功能太多,類文件也多,寫代碼時定位類文件困難,雖然當系統功能太多時,最好的選擇是將大系統拆分小系統,分別部署分散壓力,但是很多創業性公司因需求變化快,也很容易造成文件太多;

第二,按功能不同把類文件存放在不同的目錄,已經成爲幾乎所有程序員結構化思維習慣,順應這個習慣可以讓程序員更快的適應YiluPHP框架。

除了分目錄存放的習慣保留了之外,還有命名的習慣也兼容了『下劃線連接單詞』和『駝峯式』兩種命名規則,因此一個model類,即可以這樣命名 model_user,也可以這樣命名 modelUser,都是可以自動識別的。

下面是一個類的方法被調用的流程圖

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章