2009-10-16 3 views
5

Gibt es eine Möglichkeit, Ausnahmen von einem SPL Autoloader in PHP zu werfen, falls es fehlschlägt? Es scheint nicht unter PHP 5.2.11 zu funktionieren.Ausnahmen in einem SPL-Autoloader werfen?

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'; 
} 

Wenn der obige Code aufgerufen wird, gibt es keine Anzeichen für eine Ausnahme, stattdessen erhalte ich einen Standard "Fatal error: Class 'foobarDomain' nicht in bla gefunden". Und die Ausführung des Skripts endet.

+0

Was passiert genau? Du hast nur gesagt, es scheitert, aber nicht wie es scheitert. – Charles

+0

Wenn der obige Code aufgerufen wird, gibt es keine Anzeichen für eine Ausnahme, stattdessen bekomme ich einen Standard "Fataler Fehler: Klasse 'foobarDomain' nicht in bla gefunden". Und die Ausführung des Skripts endet. – clops

+0

Großartig, danke. Was passiert, wenn Sie die Ausnahme vor der Aufnahme zuerst in die Funktion werfen? – Charles

Antwort

17

Dies ist kein Fehler, dann ist es a design decision:

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

Der Grund dafür ist, dass es mehr als ein Autoload-Handler sein, wobei in diesem Fall, Sie nicht die ersten Handler wollen werfen eine Ausnahme und umgehen Sie den zweiten Handler. Sie möchten, dass Ihr zweiter Handler eine Chance hat, seine Klassen automatisch zu laden. Wenn Sie eine Bibliothek verwenden, die die Autoload-Funktion nutzt, möchten Sie nicht, dass sie den Autoload-Handler umgeht, weil sie Exceptions in ihren Autoloader werfen.

Wenn Sie möchten, ob oder nicht überprüfen Sie eine Klasse instanziiert, dann class_exists verwenden und true als zweites Argument übergeben (oder sie auslassen, ist true Standard):

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

Vielen Dank - Sie haben den Tag gespeichert! – clops

+2

Dieses Verhalten in PHP 5.3 geändert - Ausnahmen können nun geworfen und im Autoloader gefangen werden.Sie müssen jedoch vorsichtig sein, wenn Sie mehrere Autoloader registriert haben. – MrWhite

2

Laut den Kommentaren in the documentation for spl_autoload_register ist es möglich, eine andere Funktion vom Autoloader aufzurufen, die wiederum die Ausnahme auslösen würde.

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

Leider funktioniert das nicht. Der gleiche Fehler und keine Ausnahme geworfen :( – clops

1

Hier ist eine vollständige -fledged factory-Objekt, das automatisches Laden, Namespaces-Unterstützung, Callables von nicht statischen Instanzen (mit variablen Pfaden), Umgang mit Ladefehlern und benutzerdefinierten Ausnahmen demonstriert.

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).")"; 
    } 

} 

und schließlich ein Beispiel:

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'; 
    } 


} 

tun jetzt

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

oder

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

LogicException("Unknown currency provider country name Ukraine")

Wenn es keine SwissCurrencies.php Datei in/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"}')

Mit genügend Aufwand kann diese Fabrik erweitert werden, um Fehler zu fangen zu analysieren (How to catch error of require() or include() in PHP?) und Argumente an Konstruktoren übergeben.