Saltar a contenido

Espacio de nombres (Namespace)

Desde PHP 5.3 y también conocidos como Namespaces, los espacios de nombres permiten organizar las clases/interfaces, funciones y/o constantes de forma similar a los paquetes en Java.

Recomendación

Para una organización correcta del código, debes utilizar un único namespace por archivo y crear una estructura de carpetas respectando los niveles/subniveles (igual que se hace en Java)

Se declaran en la primera línea mediante la palabra clave namespace seguida del nombre del espacio de nombres asignado (cada subnivel se separa con la barra invertida \):

Por ejemplo, para colocar la clase Producto dentro del namespace Dwes\Ejemplos lo haríamos así:

ejemplonamespace.php
<?php
namespace Dwes\Ejemplos;

const IVA = 0.21;

class Producto {
    public $nombre;
      
    public function muestra() : void {
        echo"<p>Prod:" . $this->nombre . "</p>";
    }
}

Estructura de carpetas

Durante el desarrollo de proyectos en PHP, es esencial mantener una estructura de carpetas clara y coherente, especialmente cuando se usan namespaces como Dwes\Ejemplos. Muchos errores comunes entre el alumnado se deben a que los archivos no están colocados en la ruta correcta, lo que impide que las clases se carguen adecuadamente.

Para comprobar fácilmente si los archivos están en el lugar correcto, puedes usar el comando tree desde la terminal (Bash):

tree

Por ejemplo, si usas el namespace Dwes\Ejemplos, tu estructura debería verse así:

proyecto/
├── index.php
└── src/
    └── Dwes/
     └── Ejemplos/
            └── Producto.php

Así, dentro de Producto.php, podrías declarar:

namespace Dwes\Ejemplos;

class Producto {
 // ...
}

Y luego podrías usar esa clase desde index.php con:

use Dwes\Ejemplos\Producto;

Acceso

Para referenciar a un recurso que contiene un namespace, primero hemos de tenerlo disponible haciendo uso de include o require. Si el recurso está en el mismo namespace, se realiza un acceso directo (se conoce como acceso sin cualificar).

Realmente hay tres tipos de acceso:

  • sin cualificar: recurso.
  • cualificado: rutaRelativa\recurso no hace falta poner el namespace completo.
  • totalmente cualificado: \rutaAbsoluta\recurso.
Dwes\Ejemplos\ejemploNamespace2.php
<?php
namespace Dwes\Ejemplos;

include_once("Producto.php");

echo IVA; // sin cualificar
echo Utilidades\IVA; // acceso cualificado. Daría error, no existe \Dwes\Ejemplos\Utilidades\IVA
echo \Dwes\Ejemplos\IVA; // totalmente cualificado

$p1 = new Producto(); // lo busca en el mismo namespace y encuentra \Dwes\Ejemplos\Producto
$p2 = new Model\Producto(); // daría error, no existe el namespace Model. Está buscando \Dwes\Ejemplos\Model\Producto
$p3 = new \Dwes\Ejemplos\Producto(); // \Dwes\Ejemplos\Producto

Para evitar la referencia cualificada podemos declarar el uso mediante use (similar a hacer import en Java). Se hace en la cabecera, tras el namespace:

Los tipos posibles son:

  • use const nombreCualificadoConstante.
  • use function nombreCualificadoFuncion.
  • use nombreCualificadoClase.
  • use nombreCualificadoClase as NuevoNombre utilizado para renombrar elementos.

Por ejemplo, si queremos utilizar la clase \Dwes\Ejemplos\Producto desde un recurso que se encuentra en la raíz, por ejemplo en inicio.php, haríamos:

ejemplonamespace3.php
<?php
include_once("Dwes\Ejemplo\Producto.php");

use const Dwes\Ejemplos\IVA;
use \Dwes\Ejemplos\Producto;

echo IVA;
$p1 = new Producto();

To use or not to use

En resumen, use permite acceder sin cualificar a recursos que están en otro namespace. Si estamos en el mismo espacio de nombre, no necesitamos use.

Organización

Todo proyecto, conforme crece, necesita organizar su código fuente. Se plantea una organización en la que los archivos que interactuan con el navegador se colocan en el raíz, y las clases que definamos van dentro de un namespace (y dentro de su propia carpeta src o app).

Organización, includes y usos

  • Colocaremos cada recurso en un fichero aparte.
  • En la primera línea indicaremos su namespace (si no está en el raíz).
  • Si utilizamos otros recursos, haremos un include_once de esos recursos (clases, interfaces, etc...).
    • Cada recurso debe incluir todos los otros recursos que referencie: la clase de la que hereda, interfaces que implementa, clases utilizadas/recibidas como parámetros, etc...
  • Si los recursos están en un espacio de nombres diferente al que estamos, emplearemos use con la ruta completa para luego utilizar referencias sin cualificar.

Autoload

¿No es tedioso tener que hacer el include de las clases? El autoload viene al rescate.

Así pues, permite cargar las clases (no las constantes ni las funciones) que se van a utilizar y evitar tener que hacer el include_once de cada una de ellas. Para ello, se utiliza la función spl_autoload_register

ejemploautoload.php
<?php
spl_autoload_register( function( $nombreClase ) {
    include_once $nombreClase.'.php';
} );

¿Por qué se llaman autoload?

Porque antes se realizaba mediante el método mágico __autoload(), el cual está deprecated desde PHP 7.2

Y ¿cómo organizamos ahora nuestro código aprovechando el autoload?

Para facilitar la búsqueda de los recursos a incluir, es recomendable colocar todas las clases dentro de una misma carpeta. Nosotros la vamos a colocar dentro de app (más adelante, cuando estudiemos Laravel veremos el motivo de esta decisión). Otras carpetas que podemos crear son test para colocar las pruebas PhpUnit que luego realizaremos, o la carpeta vendor donde se almacenarán las librerías del proyecto (esta carpeta es un estándar dentro de PHP, ya que Composer la crea automáticamente).

Como hemos colocado todos nuestros recursos dentro de app, ahora nuestro autoload.php (el cual colocamos en la carpeta raíz) sólo va a buscar dentro de esa carpeta:

ejemplonautoload2.php
<?php
spl_autoload_register( function( $nombreClase ) {
    include_once "app/".$nombreClase.'.php';
} );

Autoload y rutas erróneas

En Ubuntu al hacer el include de la clase que recibe como parámetro, las barras de los namespace (\) son diferentes a las de las rutas (/). Por ello, es mejor que utilicemos el siguiente fichero autoload:

<?php
spl_autoload_register( function( $nombreClase ) {
    $ruta = "app\\".$nombreClase.'.php';
    $ruta = str_replace("\\", "/", $ruta); // Sustituimos las barras
    include_once $ruta;
} );

Actividad

  • AC 315 (RA3 / CE3d / IC1 / 3p). Genera varias clases (Producto, TV, Ropa, Juego), pero todos los archivos están sueltos en la raíz del proyecto, y un fichero prueba.phpque muestre objetos de esas clases. Tras esto:

    • Organizar correctamente el proyecto creando namespaces adecuados para cada clase.
    • Preparar una estructura de carpetas limpia y utilizando el autoload.
    • Hacer que el archivo llamado prueba.php funcione correctamente utilizando las clases reorganizadas con namespaces.