2009-10-16 5 views
5

C'è un modo per generare eccezioni da un caricatore automatico SPL in PHP nel caso in cui fallisca? Non sembra funzionare sotto PHP 5.2.11.Eccezioni di lancio in un caricatore automatico 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'; 
} 

Quando il codice di cui sopra si chiama, non v'è alcun segno di un'eccezione, invece ottengo uno standard "Fatal error: Class 'foobarDomain' non trovato nel bla". E l'esecuzione dello script termina.

+0

Cosa succede, esattamente? Hai solo detto che fallisce, ma non come fallisce. – Charles

+0

Quando viene chiamato il codice precedente, non vi è alcun segno di eccezione, ma ricevo uno standard "Errore irreversibile: classe 'foobarDomain' non trovato in bla". E l'esecuzione dello script termina. – clops

+0

Grande, grazie. Cosa succede quando si lancia l'eccezione per prima cosa nella funzione, prima dell'inserimento? – Charles

risposta

17

Questo non è un bug, è a design decision:

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

Il motivo è che ci possono essere più di un gestori di caricamento automatico, in questo caso, non si desidera che il primo gestore di buttare un'eccezione e bypassare il secondo gestore. Vuoi che il tuo secondo gestore abbia una possibilità di autoloading delle sue classi. Se si utilizza una libreria che utilizza la funzionalità di caricamento automatico, non si desidera ignorare il gestore del caricamento automatico poiché generano eccezioni all'interno del caricatore automatico.

Se si desidera verificare se è possibile creare un'istanza di una classe, quindi utilizzare class_exists e passare true come secondo argomento (o lasciarlo fuori, true è l'impostazione predefinita):

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

Grazie mille - hai salvato il giorno! – clops

+2

Questo comportamento è cambiato in PHP 5.3 - ora le eccezioni possono essere lanciate e catturate nel caricatore automatico.Tuttavia, è necessario fare attenzione se si dispone di più caricatori automatici registrati. – MrWhite

2

In base ai commenti in the documentation for spl_autoload_register, è possibile chiamare un'altra funzione dal caricatore automatico, che a sua volta genera l'eccezione.

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

Purtroppo questo non funziona. Stesso errore e nessuna eccezione generata: ( – clops

1

Ecco un pieno -fedged factory object che dimostra auto-caricamento, supporto namespace, callables da istanze non statiche (con percorsi variabili), gestione di errori di caricamento ed eccezioni personalizzate.

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

} 

e, infine, un esempio:

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


} 

ora fare

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

o

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

LogicException("Unknown currency provider country name Ukraine")

Se non c'è file di SwissCurrencies.php 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"}')

Con uno sforzo sufficiente questa fabbrica può essere esteso per catturare analizzare gli errori (How to catch error of require() or include() in PHP?) e passare gli argomenti per i costruttori.