Los espacios de nombres (namespaces) son una manera de encapsular elementos. Se pueden ver como un concepto abstracto en muchos aspectos.
Por ejemplo, en cualquier sistema operativo, los directorios sirven para agrupar ficheros relacionados, actuando así como espacios de nombres para los ficheros que contienen. Como ejemplo, el fichero foo.txt puede existir en los directorios /home/greg y /home/otro, pero no pueden coexistir dos copias de foo.txt en el mismo directorio. Además, para acceder al fichero foo.txt fuera del directorio /home/greg, se debe anteponer el nombre del directorio al nombre del fichero, empleando el separador de directorios (/) para así obtener /home/greg/foo.txt. Este mismo principio se extiende a los espacios de nombres en el mundo de la programación.
En el mundo de PHP, los espacios de nombres están diseñados para solucionar dos problemas con los que se encuentran los autores de bibliotecas y de aplicaciones al crear elementos de código reusable, tales como clases o funciones:
Los espacios de nombres se declaran utilizando la palabra reservada namespace. Un fichero que contenga un espacio de nombres debe declararlo al inicio del mismo, antes que cualquier otro código, con una excepción: la palabra reservada declare.
<?php namespace miProyecto; function hola() { echo "Hola mundo"; }
Además, todo lo que no sea código de PHP no puede preceder a la declaración del espacio de nombres, incluyendo espacios en blanco extra.
También, a diferencia de otras construcciones de PHP, se puede definir el mismo espacio de nombres en varios ficheros, permitiendo la separación del contenido de un espacio de nombres a través del sistema de ficheros.
Al igual que los directorios y ficheros, los espacios de nombres de PHP también tienen la capacidad de especificar una jerarquía de nombres de espacios de nombres. Así, un nombre de un espacio de nombres se puede definir con subniveles:
namespace miProyecto\Sub\Nivel;
Se puede hacer una simple analogía entre los espacios de nombres de PHP y el sistema de ficheros. Existen tres maneras de acceder a un fichero en el sistema de ficheros:
Se puede aplicar el mismo principio a los elementos del espacio de nombres de PHP.
Por ejemplo, supongamos que tenemos dos carpetas en llamadas carpeta1 y carpeta2. Dentro de carpeta1 tenemos dos archivos (archivo1.php y archivo2.php) y una carpeta llamada subcarpeta1. Ésta última carpeta (subcarpeta1) tendrá otro archivo llamado archivo3.php. Por último, tenemos el archivo archivo4.php dentro de la carpeta carpeta2.
En principio, todos los archivos tendrán el mismo contenido: el namespace al que pertenece y una función llamada hola().
Para simplificar, el namespace será el nombre de la carpeta donde está ubicado el archivo: carpeta1 para los archivos 1 y 2, carpeta1\subcarpeta1 para el archivo 3 y carpeta2 para el archivo 4.
La función hola mostrará la frase “Hola mundo del nombre_archivo”. Por ejemplo, el código de archivo1.php será:
namespace carpeta1; function hola(){ echo "Hola mundo de archivo 1 <br>"; }
Vamos a incluir archivo2.php en archivo1.php y ejecutar la función hola():
namespace carpeta1; include "./archivo2.php"; function hola(){ echo "Hola mundo de archivo 1 <br>"; } hola();
Fatal error: Cannot redeclare carpeta1\hola() (previously declared in /var/www/html/ejemplos/T10/carpeta1/archivo1.php:8) in /var/www/html/ejemplos/T10/carpeta1/archivo2.php on line 5
Obviamente, nos da un error, ya que estamos definiendo dos veces la misma función: hola(). Si te das cuenta, el error nos dice que no puede redeclarar la función carpeta1\hola(), con lo que, si estuvieran en espacios de nombre diferentes no tendríamos ningún problema.
Cambiamos el nombre de la función de archivo2.php a hola2(), añadimos archivo3.php y ejecutamos las 3 funciones:
namespace carpeta1; include "./archivo2.php"; include "./subcarpeta1/archivo3.php"; function hola(){ echo "Hola mundo de archivo 1 <br>"; } hola(); hola2(); subcarpeta1\hola();
Hola mundo de archivo 1 Hola mundo de archivo 2 Hola mundo de archivo 3
Igual que en los sistemas de ficheros, PHP entenderá que lo que ejecutamos en la última línea pertenece al namespace carpeta1\subcarpeta1 (es similar a utilizar rutas relativas en los sistemas de ficheros).
Por último, vamos a añadir archivo4 y ejecutar su función. En este caso tendremos que indicar el namespace completo, ya que carpeta2 no es un subnivel del namaspace carpeta1 (sería como utilizar rutas absolutas en un sistema de ficheros).
Para hacerlo, debemos empezar el namespace de la función a ejecutar con una barra invertida.
namespace carpeta1; include "./archivo2.php"; include "./subcarpeta1/archivo3.php"; include "../carpeta2/archivo4.php"; function hola(){ echo "Hola mundo de archivo 1 <br>"; } hola(); hola2(); subcarpeta1\hola(); \carpeta2\hola();
Hola mundo de archivo 1 Hola mundo de archivo 2 Hola mundo de archivo 3 Hola mundo de archivo 4
Uno de los usos más habituales de los namespaces es junto a la POO, para utilizar clases definidas en otros archivos. El problema es que tienes que anteponer el namespace al que pertenece esa clase siempre que quieras utilizarla.
Por ejemplo, supongamos que tenemos 1 archivo en carpeta2 un archivo llamado ClaseA.php con una clase definida con solo su constructor:
namespace carpeta2; class ClaseA { function __construct() { echo "objeto de la clase miClase creado"; } }
Si queremos utilizar esa clase en archivo1.php tenemos que incluir el archivo donde está definida y usar \carpeta2\ClaseA cada vez que la referenciemos.
namespace carpeta1; include "./archivo2.php"; include "./subcarpeta1/archivo3.php"; include "../carpeta2/archivo4.php"; include "../carpeta2/ClaseA.php"; function hola(){ echo "Hola mundo de archivo 1 <br>"; } hola(); hola2(); subcarpeta1\hola(); \carpeta2\hola(); $objetoA = new \carpeta2\ClaseA();
Podemos simplificar el proceso importando la clase con su espacio de nombre usando la palabra reservada use. De esta forma, ya no hará falta anteponer el namespace al que pertenece esa clase:
namespace carpeta1; use carpeta2\ClaseA; include "./archivo2.php"; include "./subcarpeta1/archivo3.php"; include "../carpeta2/archivo4.php"; include "../carpeta2/ClaseA.php"; function hola(){ echo "Hola mundo de archivo 1 <br>"; } hola(); hola2(); subcarpeta1\hola(); \carpeta2\hola(); $objetoA = new ClaseA();
También podemos utilizar un alias para la clase importada usando la palabra reservada as.
namespace carpeta1; use carpeta2\ClaseA as OtraClase; include "./archivo2.php"; include "./subcarpeta1/archivo3.php"; include "../carpeta2/archivo4.php"; include "../carpeta2/ClaseA.php"; function hola(){ echo "Hola mundo de archivo 1 <br>"; } hola(); hola2(); subcarpeta1\hola(); \carpeta2\hola(); $objetoA = new OtraClase();