miércoles, 20 de febrero de 2013

PHP Avanzado: Namespaces y funciones anónimas

PHP Tanto los namespaces como las funciones anónimas fueron novedades de PHP 5.3. En este artículo explicaré un poco que son ambas cosas, incluyendo ejemplos de uso.


Funciones anónimas en PHP:

Las funciones anónimas, también llamadas closures, son funciones sin nombre que normalmente se utilizan para crear callbacks de forma sencilla. Son ampliamente usadas en otros lenguajes como Javascript, pero PHP no las ha implementado hasta la versión 5.3. Las funciones anónimas pueden asignarse a una variable, pasarse como parámetro de otra función o ser retornadas.

$strlen = function($str) {
    return strlen($str);
}
echo $strlen('Hola Mundo');

Para que una función anónima pueda heredar las variables del padre debe usarse la palabra clave "use" con la siguiente sintaxis:

$strings = array('Hola ', 'Mundo');
$concat = null;

// Imprime "10" (el resultado de la función anónima)
echo function() use($strings, &$concat) {
    $result = 0;
    $concat = '';
    foreach($strings as $string) {
        $result += strlen($string);
        $concat .= $string;
    }
    return $result;
};

// Imprime "Hola Mundo" (lo ha cargado la función anónima)
echo $concat;

Namespaces en PHP:

Según la Wikipedia, un namespace (o "espacio de nombres") es "es un contenedor abstracto en el que un grupo de uno o más identificadores únicos pueden existir". Esto quiere decir que un identificador es único dentro del espacio de nombres, pero el mismo identificador puede existir en varios espacios de nombres. En la práctica, los namespaces se utilizan para estructurar mejor el código fuente de la aplicación.

En PHP los espacios de nombres se diseñaron para disponer de una forma de agrupar clases, interfaces, funciones y constantes relacionadas bajo un mismo nombre, manteniendo nombres comprensibles para los distintos elementos. De esta manera los diseñadores de librerías reusables evitan tener que usar nombres extra largos para diferenciar sus clases/interfaces/funciones/constantes de otras de terceros. Entre otras ventajas esto mejora la legibilidad del código fuente.

Como declarar espacios de nombres:

Para declarar un espacio de nombre se usa la palabra clave namespace. Esta sentencia debe ser la primera del archivo. Las únicas sentencias válidas antes de namespace son sentencias de tipo declare.

<?php
    namespace MiNombre;
?>

Dentro de un namespace puede incluirse una jerarquía de niveles, similar a una estructura de directorios. Las diferentes jerarquías de los espacios de nombres de separan con la barra invertida "\":

<?php
    namespace Empresa\Proyecto\Componente;
?>

Como curiosidad, en PHP se puede definir el mismo espacio de nombres en distintos archivos, cosa que no suele estar permitida en otros lenguajes. También se puede declarar más de un namespace en el mismo archivo, aunque está totalmente desaconsejado.

Como usar los espacios de nombres:

Para usar un identificador declarado en un espacio de nombres desde fuera del namespace (por ejemplo una clase) hay que usar la jerarquía completa, comenzando por la barra invertida. A esto se le llama nombre completamente cualificado. También se pueden usar nombres relativos al namespace actual, de forma análoga al sistema de archivos de una PC que está formado por directorios, subdirectorios y archivos. Por ejemplo supongamos que tenemos definida la clase "Clase" en el espacio de nombres "Empresa\Proyecto\Componente". La forma de crear un objeto de esa clase sería:

namespace {
    $class = new \Empresa\Proyecto\Componente\Clase();
}

namespace Empresa {
    $class = new Proyecto\Componente\Clase();
}

namespace Empresa\Proyecto {
    $class = new Componente\Clase();
}

namespace Empresa\Proyecto\Componente {
    $class = new Clase();
}

Para acceder a cualquier clase, función o constante globales (esto es: definidas fuera de cualquier espacio de nombres), se puede usar un nombre completamente cualificado empezando por la barra invertida. Por ejemplo:

namespace Proyecto {
    class String {
        private $str;
        // (...)
        public function strlen() {
            return \strlen($str);
        }
    }
}

Otra forma de acceder a los identificadores de un namespace es a través de la palabra clave "use", la cual permite "importar" los identificadores de un espacio de nombres sobre el namespace actual. Sería algo similar al establecimiento de un enlace simbólico con otro espacio de nombres. La palabra clave use también permite definir un alias sobre un nombre de clase o interfaz.

namespace Empresa\Proyecto1 {
    class Clase {
        // (...)
    }
}

namespace Empresa\Proyecto2 {

    // Importa el namespace completo.
    use Empresa\Proyecto1;

    // Crea un objeto de clase \Empresa\Proyecto1\Clase
    $clase = new Proyecto1\Clase();
}

namespace Empresa\Proyecto3 {

    // Importa sólo una clase y le asigna un alias
    use Empresa\Proyecto1\Clase as Clase1;

    // Crea un objeto de clase \Empresa\Proyecto1\Clase
    $clase = new Clase1();
}

Para resolver ambigüedades a la hora de acceder a un identificador del espacio de nombres propio cuando existen otros identificadores iguales importados de otros namespaces se puede usar la palabra clave "namespace":

namespace Empresa\Proyecto1 {
    class Clase {
        // (...)
    }
}

namespace Empresa\Proyecto2 {
    use Empresa\Proyecto1\Clase;

    class Clase {
        // (...)
    }

    // Crea un objeto de clase \Empresa\Proyecto1\Clase
    $clase1 = new Clase();

    // Crea un objeto de clase \Empresa\Proyecto2\Clase
    $clase1 = new namespace\Clase();
}

Enlaces y referencias:

No hay comentarios:

Publicar un comentario