A free and open-source book on ZF3 for beginners

Translation into this language is not yet finished. You can help this project by translating the chapters and contributing your changes.

3.4. La Clase Autoloading de PHP

Una aplicación web consiste en muchas clases PHP y cada clase generalmente reside en un archivo separado. Esto crea la necesidad de incluir los archivos.

Por ejemplo, vamos a asumir que tenemos el archivo llamado Application.php que contiene la definición para la clase \Zend\Mvc\Application de la sección anterior. Antes de que podamos crear una instancia de la clase Application en cualquier lugar de nuestro código debemos incluir el contenido del archivo Application.php (podemos hacer esto con la ayuda de la sentencia require_once, pasándole la ruta completa del archivo):

<?php
require_once "/path/to/zendframework/zend-mvc/src/Application.php";

use Zend\Mvc\Application;

$application = new Application();

Como la aplicación crece en tamaña puede ser difícil incluir cada nuevo archivo. Zend Framework 3 consiste en cientos de archivos y puede ser muy difícil cargar la biblioteca entera y todas sus dependencias. Además, cuando se ejecuta el código resultante el interprete PHP consumirá tiempo de CPU para procesar cada archivo incluido, aun cuando no se crea una instancia de su clase.

Para corregir este problema se introdujo en PHP una característica que permite la carga automática de clases (class autoloading). La función de PHP spl_autoload_register() permite registrar una función autoloader. En sitios webs complejos podemos crear varias funciones autoloader que se encadenan en una pila.

Durante la ejecución de un script si el interprete de PHP encuentra el nombre de una clase que no se ha definido aún, se llama una por una a todas las funciones autoloader registradas hasta encontrar a la función autoloader que tiene la clase o hasta que se alcanza un error «not fount». Esto permite una lazy loading, el interprete PHP procesa la definición de la clase solo en el momento de su invocación, cuando es realmente necesario.

3.4.1. La Clase «Map Autoloader»

Para dar una idea de como es una función autoloader, mostraremos una implementación simplificada de una función autoloader:

<?php
// Autoloader function.
function autoloadFunc($className)
{
    // Class map static array.
    static $classMap = [
        '\\Zend\\Mvc\\Application' => '/path/to/zendframework/zend-mvc/src/Zend/Mvc/Application.php',
        '\\Application\\Module' => '/path/to/app/dir/module/Application/Module.php',
        //...
    ];

    // Check if such a class name presents in the class map.
    if(isset(static::$classMap[$className])) {
        $fileName = static::$classMap[$className];

        // Check if file exists and is readable.
        if (is_readable($fileName)) {
            // Include the file.
            require $fileName;
        }
    }
}

// Register our autoloader function.
spl_autoload_register("autoloadFunc");

En el ejemplo de arriba definimos la función autoloader autoloadFunc(), que llamaremos de ahora en adelante clase map autoloader.

La clase map autoloader usa la clase map para hacer una correspondencia entre el nombre de la clase y la ruta absoluta al archivo PHP que contiene la clase. La clase map es un típico arreglo de PHP que contiene llaves y valores. Para determinar la ruta del archivo para cada nombre de clase, la clase map autoloader necesita traer el valor desde el arreglo $classMap. Es obvio que la clase map autoloader trabaja muy rápido. Sin embargo, su desventaja es que tenemos que mantener a la clase map actualizada, lo que es necesario cada vez que agregamos una nueva clase a nuestro programa.

3.4.2. El Estándar PSR-4

A causa de que cada biblioteca usa su propia nomenclatura para el código y sus propias convenciones para la organización de los archivos, tendremos que registrar una función autoloader diferente y personalizada por cada biblioteca que usemos, lo que es bastante molesto (y de hecho un trabajo innecesario). Para resolver este problema, el estándar PSR-4 fue introducido.

PSR significa en ingles «PHP Standards Recommendation».

El estándar PSR-4 define la estructura del código recomendada que una aplicación o un biblioteca debe seguir para garantizar la interoperabilidad entre autoloaders. En dos palabras el estándar dice que:

Por ejemplo, para la clase Zend\Mvc\Application tendremos la siguiente estructura de directorios:

/path/to/zendframework/zend-mvc/src
  /Zend
    /Mvc
       Application.php

La desventaja de esto es que es necesario colocar el código dentro de varios directorios anidados (Zend y Mvc).

Para corregir esto PSR-4 permite que definamos una serie continua de uno o más namespace o sub-namespaces iniciales que correspondan a una «carpeta base». Por ejemplo, si tenemos el nombre de clase fully qualified: Zend\Mvc\Application, y definimos que la serie Zend\Mvc corresponde a la carpeta "/path/to/zendframework/zend-mvc/src" podemos organizar los archivos de la siguiente manera:

/path/to/zendframework/zend-mvc/src
    Application.php

Para que el código esté conforme al estándar PSR-4, podemos escribir y registrar un autoloader que llamaremos autoloader «standard»:

<?php

// "Standard" autoloader function.
function standardAutoloadFunc($className)
{
    // Replace the namespace prefix with base directory.
    $prefix = '\\Zend\\Mvc';
    $baseDir = '/path/to/zendframework/zend-mvc/src/';
    if (substr($className, 0, strlen($prefix)) == $prefix) {
        $className = substr($className, strlen($prefix)+1);
        $className = $baseDir . $className;
    }

    // Replace namespace separators in class name with directory separators.
    $className = str_replace('\\', DIRECTORY_SEPARATOR, $className);

    // Add the .php extension.
    $fileName = $className . ".php";

    // Check if file exists and is readable.
    if (is_readable($fileName)) {
        // Include the file.
        require $fileName;
    }
}

// Register the autoloader function.
spl_autoload_register("standardAutoloadFunc");

El autoloader estándar funciona de la siguiente manera. Asumiendo que el namespace de la clase se corresponde uno-a-uno a la estructura de directorios, la función calcula la ruta hasta el archivo PHP transformando las barras invertidas (separadores de namesapaces) a barras (separadores de ruta) y concatena la ruta resultante a la ruta absoluta del directorio donde la biblioteca está ubicada. Luego, la función revisa si el archivo PHP existe y si es así incluye el archivo usando la sentencia require.

Es obvio que el autoloader estándar funciona más lento que la clase map autoloader. Sin embargo, su ventaja es que no necesitamos mantener ninguna clase map, lo que es muy conveniente cuando desarrollamos código nuevo y agregamos nuevas clases a la aplicación.

Zend Framework 3 se ajusta al estándar PSR-4, haciendo posible usar el mecanismo de autoloading estándar con todos sus componentes. Es también compatible con otras bibliotecas que se ajustan a PSR-4 como Doctrine o Symfony.

3.4.3. El Autoloader provisto por Composer

Composer puede generar las funciones de autoloader (tanto la clase map autoloaders como el estándar de autoloaders PSR-4) para el código que se instala con él. Zend Framework 3 usa la implementación que hace Composer del «autoloader». Cuando se instala un paquete usando Composer se crea automáticamente el archivo APP_DIR/vendor/autoload.php, que usa la función de PHP spl_autoload_register() para registrar un autoloader. De esta manera todas las clases de PHP ubicadas en el directorio APP_DIR/vendor se cargan correctamente.

Para autocargar las clases de PHP ubicadas en nuestro propios módulos (por ejemplo, el módulo Application) tendremos que especificar la llave autoload en nuestro archivo composer.json.

"autoload": {
    "psr-4": {
        "Application\\": "module/Application/src/"
    }
},

Luego la única cosa que necesitamos hacer es incluir este archivo en nuestro script de entrada index.php:

// Composer autoloading
include __DIR__ . '/../vendor/autoload.php';

El archivo autoload.php se genera cada vez que se instala un paquete usando Composer. Además, podemos ejecutar el comando dump-autoload para que Composer genere el archivo autoload.php.

php composer.phar dump-autoload

3.4.4. PSR-4 y Estructura de la Carpeta Fuente del Módulo

En la aplicación Zend Skeleton podemos ver como el estándar PSR-4 se aplica en la práctica. Para el módulo por defecto de nuestro sitio web, el módulo Application, las clases PHP que se registran en el autoloader estándar se guardan en el directorio APP_DIR/module/Application/src («src» es la abreviatura de «source»).

Llamaremos a la carpeta src: carpeta fuente del módulo.

Por ejemplo, vamos a ver el archivo IndexController.php del módulo Application (figura 3.2).

Figura 3.2. Estructura de carpetas de la aplicación Skeleton conforme al estándar PSR-4 Figura 3.2. Estructura de carpetas de la aplicación Skeleton conforme al estándar PSR-4

Como podemos ver este contiene la clase 1 IndexController que pertenece al namespace Application\Controller. Para seguir el estándar PSR-4 y usar el autoloader estándar con esta clase PHP tenemos que colocarla dentro de la carpeta Controller que está dentro del directorio fuente del módulo.

1) La clase IndexController es el controlador por defecto para la aplicación skeleton. Hablaremos luego sobre los controladores en el capítulo Model-View-Controller.


Top