2011-11-03 39 views
1

爲了更深入地理解MVC範例,我正在試驗構建自己的框架。捕獲自定義spl_autloader函數錯誤的最佳方法

我沒有依賴於由於包含失敗而導致的PHP醜陋錯誤/警告,而是設置了一個通用的異常類,以一種更易讀的格式顯示數據以及一個堆棧跟蹤。

這裏是我到目前爲止的代碼...

spl_autoload_register(__NAMESPACE__.'\Autoloader::coreLoader'); 
spl_autoload_register(__NAMESPACE__.'\Autoloader::appLoader'); 
spl_autoload_register(__NAMESPACE__.'\Autoloader::throwException'); 

class Autoloader 
{ 
    private static $isError = false; 

    private static function loadHelper($className) 
    { 
     //Relevant code here 
    } 

    public static function coreLoader($className) 
    { 
     $classPath = static::loadHelper($className); 

     if ([email protected] PRIVATE_ROOT.DIRECTORY_SEPARATOR.$classPath.PHPEXT) 
     { 
      static::$isError = true; 
     } 
     else 
     { 
      static::$isError = false; 
     } 
    } 

    public static function appLoader($className) 
    { 
     $classPath = static::loadHelper($className); 

     if ([email protected] SYSTEM_PATH.DIRECTORY_SEPARATOR.$classPath.PHPEXT) 
     { 
      static::$isError = true; 
     } 
     else 
     { 
      static::$isError = false; 
     } 
    } 

    public static function throwException($className) 
    { 
     if (static::$isError) 
     { 
      throw new Exception_General('Attempted to load file: '.$className); 
     } 
    } 
} 

鑑於include不會產生一個異常時,未找到文件,我不能使用try/catch塊的事實。

代替try/catch塊,我發現可以使用上面代碼中的if statement來檢查include語句是否成功加載所需的文件。

我的Exception_General類負責生成和顯示開發人員友好的錯誤輸出/消息。但我在這裏遇到的問題是,如果我在合法的自動加載方法中拋出一個Exception,該腳本將正確地停止。

這當然是不理想的,因爲雖然第一個自動加載方法可能找不到請求的類,但spl_autoload隊列中的第二個或第三個自動加載方法MIGHT能夠找到請求的文件/類。

爲了適應這種行爲,我發現我必須創建第三個「僞」自動加載方法,它是隊列中最後一個被調用的方法 - 此方法檢查錯誤標誌,如果設置了,拋出異常。

在一個漫長的問題中,我真正要問的是 - 是否有更好的方法來捕獲失敗的包含,並且一旦所有的自動加載函數都運行正常,行爲恰當?

回答

0

聲明:我不一定認爲這是一個很好的解決方案,特別是一些回調會被重複,但我會把它作爲一個潛在的靈感來源。

我剛剛有一個小提琴與此並提出了一個自動加載方法,如果它在自動加載嘗試中使用,將拋出一個異常在序列的末尾,如果沒有找到類。它通過在自動加載過程發生時修改自動加載堆棧並遞歸調用自動加載來實現。所有這些魔術的好處是,只有在所有回調失敗的情況下才會拋出異常,並且不需要虛擬回調,這會影響後續註冊的回調。與此

function a() { echo "a\n"; } 
function b() { echo "b\n"; } 

class Autoloader { 
    // This prevents infinite recursion. 
    static $loading = false; 

    // This is the callback for this autoloader, for convenience. 
    static $callback = ['Autoloader', 'autoload']; 

    static function autoload($class_name) { 
    if(!static::$loading) { 
     // Basically what we do here is repeat the autoload cycle, and if it fails 
     // throw an exception. 
     $autoloaders = spl_autoload_functions(); // All registered autoloaders. 
     $priority = array_search(static::$callback, $autoloaders); 
     $past = []; // An array for callbacks that have already occurred. 
     for($i = 0; $i < $priority; $i++) { 
     $past[] = $autoloaders[$i]; // Fill the past events 
     spl_autoload_unregister($autoloaders[$i]); // Remove it from the stack 
     } 
     // We have now taken off the callbacks that ran before this one 

     static::$loading = true; // Make sure we don't get stuck. 
     spl_autoload_call($class_name); // 'Resume' autoloading. 
     static::$loading = false; // Reset for next time. 

     // We now need to put the other callbacks back in. 
     for($i = count($past) - 1; $i >= 0; $i--) 
     spl_autoload_register($past[$i], true, true); 

     if(class_exists($class_name)) 
     return true; // All is well 
     throw new Exception('could not find class '.$class_name); // Not so well... 
    } else { 
     echo "Autoloader::autoload\n"; 
    } 
    } 
} 

spl_autoload_register('a'); 
spl_autoload_register(Autoloader::$callback); 
spl_autoload_register('b'); 

new Foo; // Would print `a, Autoloader::autoload, b` then the exception message. 

一個需要注意的是,Autoloader::autoload後出現回調會發生兩次,因爲異常不會真正打出來的自動加載鏈。雖然可以在引發異常之前刪除這些回調,但如果捕獲到異常,則會中斷自動加載序列(例如,如果['a', 'Autoloader...', 'c']位於堆棧上且未找到類,則將在代碼中執行兩次c或者可以修改代碼以使c發生一次,但是如果發現異常,自動載入堆棧將保留爲['a', 'Autoloader...'])。

+0

我選擇走多個自動加載機的路線的原因之一是性能/速度。如果只使用單個自動加載函數的路由,並且在該函數內解析/匹配類名稱字符串,那麼拋出異常就沒有問題,因爲您只有一個包含要與之競爭。這種方法的缺點是你爲簡單的加載類發揮了很大的處理能力。對於多個自動加載器,由於每個自動加載器都負責特定的類/路徑段,所以沒有太多處理髮生。 – cgons

+0

當然,你是對的。我不認爲這是我在生產環境中使用的東西,因爲如你所說,找到每個班級的開銷很大。這或多或少受到確保只有在所有回調失敗時才拋出異常的想法的驅動(甚至是在Autoloader之後添加的異常)我可能稍後再看看它,看看我能否改進它(已當我發佈它時,已經過去了我的睡覺時間)。 – connec

相關問題