domingo, 9 de octubre de 2016

Carga automática de clases. Parte I.

Es frecuente encontrar, al comienzo de ficheros php, largas listas de requires con las clases que se van a utilizar. Y en esos otros ficheros que requerimos encontramos a su vez otras listas de requerimientos. Es molesto tener que escribirlos todos, o borrarlos si movemos instanciaciones, pero tiene solución creando un cargador de clases.

Parte I. Autoload simple.



Un sencillo ejemplo en el cual todos los ficheros php están en el mismo directorio.

Tenemos el siguiente fichero php:
[code] require_once "Account.php"; require_once "Output.php"; try { $account = new Account(5); $output = Output::getOutput(output::CONSOLE); $account->addAmount(2000); $account->payCommision(); $account->addAmount(50); $account->recoverAmount(20); $output->print($account->getTotal()); } catch (Exception $e) { echo($e->getMessage().PHP_EOL); } [/code]
Vemos que utiliza 2 clases: Account y Output. Este caso es muy simple, pero si utilizáramos 50 clases deberíamos hacer un listado con 50 requires. Una locura no apta para dedos sensibles a escribir una y otra vez cosas que no aportan nada.

¿Qué no aporta nada? ¿Y qué hacemos entonces? Que si lo quito porque no quiero escribirlo me salta el siguiente error:
[code] ‘Fatal error: Class 'Account' not found’ [/code]
Pues aprovechemos la siguiente función de php: spl_autoload_register.

Vamos a crear un fichero básico (muy básico) llamado autoload_basic.php.
[code] spl_autoload_register(function ($class) { echo("Requiriendo fichero: ".$class.".php".PHP_EOL); require_once $class.".php"; }); [/code]
Si no entiendes muy bien qué ocurre, y por qué hay una función cómo parámetro, tiene que ver con las closures. ¿El qué? Es tema para otro post, pero quedémonos con el siguiente resumen: el parámetro que acepta la función spl_autoload_register es de tipo callable, es decir, lo que se le pasa es una llamada de retorno. La función que se pasa como parámetro es una closure o función anónima, y su tipo es Closure. Las funciones anónimas, es decir, los tipos Closure también se pueden pasar como un parámetro callable.

La función que se registra en spl_autoload_register es el autoload que queremos que se ejecute cada vez que se quiere instanciar una clase.

Cambiemos la función principal:
[code hl="1"] require_once "autoload_basic.php"; try { $account = new Account(5); $output = Output::getOutput(output::CONSOLE); $account->addAmount(2000); $account->payCommision(); $account->addAmount(50); $account->recoverAmount(20); $output->print($account->getTotal()); } catch (Exception $e) { echo($e->getMessage().PHP_EOL); } [/code]
Ya está, todo listo para probar.

Fijemos antes que los requires a cada fichero han desaparecido y ahora estamos haciendo un único require de autoload_basic.php.

Ejecutemos:
[code] Requiriendo fichero: Account.php Requiriendo fichero: Output.php Total amount is: 1930 [Finished in 0.1s] [/code]
Con este resultado sacamos la siguiente conclusión:
  1. El ejecutable trata de instanciar una clase.
  2. El fichero que contiene la clase no ha sido requerido, por lo que se ejecuta la función autoad que se ha registrado a través de spl_autoload_register.
  3. La variable $class es el nombre de la clase que se trata de instanciar.
  4. Dentro de la función autoload se hace el require de la clase.
  5. Ya no hay que hacer x requires en el fichero principal.

Todo perfecto y en orden, salvo que todo no puede ser siempre tan simple.

NOTA: El fichero autoload_basic.php, para este caso, podría ser de la siguiente forma:
[code] spl_autoload_register(); [/code]
Si la función spl_autoload_register no recibe ningún parámetro usa directamente como variable callable por defecto la función spl_autoload que a su vez tiene como parámetro el nombre de la clase que se instancia. He preferido añadir la closure para que tenga el mismo efecto pero que se vea qué hace.

Código en GitHub

No hay comentarios:

Publicar un comentario