2009-10-16 17 views
5

¿Hay alguna manera de lanzar excepciones desde un autocargador SPL en PHP en caso de que falle? No parece funcionar en PHP 5.2.11.¿Desea lanzar excepciones en un autocargador 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'; 
} 

Cuando el código de arriba se llama, no hay ninguna señal de una excepción, en vez consigo un estándar "Fatal error: 'foobarDomain' Clase no encontrada en bla". Y la ejecución del script termina.

+0

¿Qué ocurre exactamente? Usted solo ha dicho que falla, pero no cómo falla. – Charles

+0

Cuando se llama al código anterior, no hay signos de excepción, en su lugar obtengo un "Error fatal: Clase 'foobarDomain' no encontrado en bla". Y la ejecución del script termina. – clops

+0

Genial, gracias. ¿Qué sucede cuando lanzas la excepción primero en la función, antes de la inclusión? – Charles

Respuesta

17

Esto no es un error, es a design decision:

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

La razón es que no puede haber más de uno manipuladores de carga automática, en cuyo caso, que no desee el primer controlador de tirar una excepción y eludir el segundo controlador. Desea que su segundo controlador tenga la oportunidad de cargar automáticamente sus clases. Si usa una biblioteca que hace uso de la función de carga automática, no quiere que pase por alto su controlador de carga automática porque arrojan Excepciones dentro de su autocargador.

Si desea comprobar si está o no puede crear una instancia de una clase, a continuación, utilizar class_exists y pasar true como segundo argumento (o dejarlo fuera, true es el valor predeterminado):

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

Muchas gracias, ¡salvó el día! – clops

+2

Este comportamiento cambió en PHP 5.3: las excepciones ahora se pueden lanzar y atrapar en el autocargador.Sin embargo, debe tener cuidado si tiene varios cargadores automáticos registrados. – MrWhite

2

De acuerdo con los comentarios en the documentation for spl_autoload_register, es posible llamar a otra función desde el autocargador, que a su vez arrojaría la excepción.

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

Lamentablemente, esto no funciona. El mismo error y ninguna excepción arrojada :( – clops

1

Aquí hay una completa - objeto de fábrica encuadernado que demuestra la carga automática, soporte de espacios de nombres, llamadas de instancias no estáticas (con rutas de acceso variables), manejo de errores de carga y excepciones personalizadas.

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

} 

y, finalmente, un ejemplo:

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


} 

ahora hacer

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

o

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

LogicException("Unknown currency provider country name Ukraine")

Si no hay SwissCurrencies.php archivo en/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 bastante esfuerzo esta fábrica se puede extender a coger analizar errores (How to catch error of require() or include() in PHP?) y pasar argumentos a los constructores.

Cuestiones relacionadas