2009-10-16 29 views
5

有沒有辦法在PHP中失敗時從SPL自動加載器中拋出異常?它似乎沒有在PHP 5.2.11下工作。在SPL自動加載器中拋出異常?

class SPLAutoLoader{ 

    public static function autoloadDomain($className) { 
     if(file_exists('test/'.$className.'.class.php')){ 
      require_once('test/'.$className.'.class.php'); 
      return true; 
     }  

     throw new Exception('File not found'); 
    } 

} //end class 

//start 
spl_autoload_register(array('SPLAutoLoader', 'autoloadDomain')); 

try{ 
    $domain = new foobarDomain(); 
}catch(Exception $c){ 
    echo 'File not found'; 
} 

當上面的代碼被調用時,沒有異常的跡象,相反,我得到一個標準的「致命錯誤:類‘foobarDomain’BLA中未找到」。腳本的執行終止。

+0

究竟發生了什麼?你只是說它失敗了,但不是它失敗了。 – Charles

+0

當上面的代碼被調用時,沒有任何異常的跡象,而是我得到了一個標準的「致命錯誤:在bla中找不到類'foobarDomain'」。腳本的執行終止。 – clops

+0

太好了,謝謝。在包含之前在函數中拋出異常第一件事時會發生什麼? – Charles

回答

17

這是不是一個錯誤,這是a design decision

Note: Exceptions thrown in __autoload function cannot be caught in the catch block and results in a fatal error.

的原因是,可能有不止一個自動加載的處理程序,在這種情況下,你不希望第一個處理程序拋出異常並繞過第二個處理程序。你希望你的第二個處理程序有機會自動加載它的類。如果您使用使用自動加載功能的庫,則不希望它繞過自動加載處理程序,因爲它們會在自動加載程序中引發Exceptions。

如果要檢查你是否能實例化一個類,然後使用class_exists並通過true作爲第二個參數(或離開它,true是默認值):

if (class_exists('foobarDomain', $autoload = true)) { 
    $domain = new foobarDomain(); 
} else { 
    echo 'Class not found'; 
} 
+0

非常感謝 - 你保存了一天! – clops

+2

這種行爲在PHP 5.3中改變 - 現在可以拋出異常並在自動裝載器中捕獲異常。但是,如果您註冊了多個自動加載器,則需要小心。 – MrWhite

2

根據the documentation for spl_autoload_register中的意見,可能會從自動加載器中調用另一個函數,而這又會拋出異常。

class SPLAutoLoader{ 

    public static function autoloadDomain($className) { 
     if(file_exists('test/'.$className.'.class.php')){ 
      require_once('test/'.$className.'.class.php'); 
      return true; 
     }  
     self::throwFileNotFoundException(); 
    } 

    public static function throwFileNotFoundException() 
    { 
     throw new Exception('File not found'); 
    } 

} //end class 

//start 
spl_autoload_register(array('SPLAutoLoader', 'autoloadDomain')); 

try{ 
    $domain = new foobarDomain(); 
}catch(Exception $c){ 
    echo 'File not found'; 
} 
+0

不幸的是,這不起作用。同樣的錯誤,並沒有拋出異常:( – clops

1

這裏是一個完整的它演示了自動加載,名稱空間支持,可從非靜態實例(具有可變路徑)進行可調用,處理加載錯誤和自定義異常。

abstract class AbstractFactory implements \ArrayAccess 
{ 
    protected $manifest; 
    function __construct($manifest) 
    { 
     $this->manifest = $manifest; 
    } 

    abstract function produce($name); 

    public function offsetExists($offset) 
    { 
     return isset($this->manifest[$offset]); 
    } 

    public function offsetGet($offset) 
    { 
     return $this->produce($offset); 
    } 
    //implement stubs for other ArrayAccess funcs 
} 


abstract class SimpleFactory extends AbstractFactory { 

    protected $description; 
    protected $path; 
    protected $namespace; 

    function __construct($manifest, $path, $namespace = "jj\\") { 
     parent::__construct($manifest); 
     $this->path = $path; 
     $this->namespace = $namespace; 
     if (! spl_autoload_register(array($this, 'autoload'), false)) //throws exceptions on its own, but we want a custom one 
      throw new \RuntimeException(get_class($this)." failed to register autoload."); 
    } 

    function __destruct() 
    { 
     spl_autoload_unregister(array($this, 'autoload')); 
    } 

    public function autoload($class_name) { 
     $file = str_replace($this->namespace, '', $class_name); 
     $filename = $this->path.$file.'.php'; 
     if (file_exists($filename)) 
      try { 
       require $filename; //TODO add global set_error_handler and try clause to catch parse errors 
      } catch (Exception $e) {} //autoload exceptions are not passed by design, nothing to do 
    } 

    function produce($name) { 
     if (isset($this->manifest[$name])) { 
      $class = $this->namespace.$this->manifest[$name]; 
      if (class_exists($class, $autoload = true)) { 
       return new $class(); 
      } else throw new \jj\SystemConfigurationException('Factory '.get_class($this)." was unable to produce a new class {$class}", 'SYSTEM_ERROR', $this); 
//an example of a custom exception with a string code and data container 

     } else throw new LogicException("Unknown {$this->description} {$name}."); 
    } 

    function __toString() //description function if custom exception class wants a string explanation for its container 
    { 
     return $this->description." factory ".get_class($this)."(path={$this->path}, namespace={$this->namespace}, map: ".json_encode($this->manifest).")"; 
    } 

} 

最後一個例子:

namespace jj; 
require_once('lib/AbstractFactory.php'); 
require_once('lib/CurrenciesProvider.php'); //base abstract class for all banking objects that are created 

class CurrencyProviders extends SimpleFactory 
{ 
    function __construct() 
    { 
     $manifest = array(
      'Germany' => 'GermanBankCurrencies', 
      'Switzerland' => 'SwissBankCurrencies' 
     ); 

     parent::__construct($manifest, __DIR__.'/CurrencyProviders/', //you have total control over relative or absolute paths here 
     'banks\'); 
     $this->description = 'currency provider country name'; 
    } 


} 

現在要做的

$currencies_cache = (new \jj\CurrencyProviders())['Germany']; 

$currencies_cache = (new \jj\CurrencyProviders())['Ukraine']; 

LogicException("Unknown currency provider country name Ukraine")

如果沒有SwissCurrencies.php文件/ CurrencyProviders /,

\jj\SystemConfigurationException('Factory jj\CurrencyProviders was unable to produce a new class banks\SwissCurrencies. Debug data: currency provider country name factory jj\CurrencyProviders(path=/var/www/hosted/site/.../CurrencyProviders/, namespace=banks\, map: {"Germany": "GermanBankCurrencies", "Switzerland":"SwissBankCurrencies"}')

有了足夠的努力,這個工廠可以擴展到趕上解析錯誤(How to catch error of require() or include() in PHP?),並通過參數的構造函數。