Lección 53 de 75 10 min de lectura

Introducción a Namespaces

Los namespaces son la forma de organizar y agrupar código en PHP. Resuelven el problema de colisiones de nombres cuando usas múltiples librerías o trabajas en proyectos grandes.

El problema sin namespaces

Imagina que estás usando dos librerías diferentes. Ambas definen una clase llamada Usuario. Sin namespaces, PHP no sabría cuál usar:

PHP
<?php

// libreria1/Usuario.php
class Usuario
{
    public function __construct(
        public readonly string $nombre
    ) {}
}

// libreria2/Usuario.php
class Usuario  // ERROR: No se puede redeclarar la clase Usuario
{
    public function __construct(
        public readonly int $id,
        public readonly string $email
    ) {}
}

Esto genera un error fatal. Los namespaces resuelven este problema creando "espacios" separados para cada grupo de código.

¿Qué es un namespace?

Un namespace es como una carpeta virtual que agrupa clases, interfaces, funciones y constantes relacionadas. Piénsalo como los directorios en tu sistema de archivos: puedes tener un archivo config.php en diferentes carpetas sin conflicto.

PHP
<?php

// src/Auth/Usuario.php
namespace Auth;

declare(strict_types=1);

class Usuario
{
    public function __construct(
        public readonly string $nombre,
        public readonly string $password
    ) {}

    public function verificarPassword(string $intento): bool
    {
        return password_verify($intento, $this->password);
    }
}
PHP
<?php

// src/Blog/Usuario.php
namespace Blog;

declare(strict_types=1);

class Usuario
{
    public function __construct(
        public readonly int $id,
        public readonly string $nombrePublico,
        public readonly string $bio
    ) {}
}

Ahora tenemos dos clases Usuario que coexisten sin problemas: Auth\Usuario y Blog\Usuario.

Declarar un namespace

El namespace se declara al inicio del archivo, antes de cualquier otro código (excepto declare):

PHP
<?php

namespace MiProyecto\Servicios;

declare(strict_types=1);

class EmailService
{
    public function enviar(string $destinatario, string $mensaje): bool
    {
        // Lógica para enviar email
        return true;
    }
}
Orden correcto

El namespace debe ir primero (después de <?php), seguido de declare(strict_types=1). Nada más puede ir antes del namespace.

Namespaces anidados

Los namespaces pueden tener múltiples niveles, separados por \:

PHP
<?php

// Estructura típica de proyecto
namespace App\Http\Controllers;      // Para controladores
namespace App\Models;                // Para modelos
namespace App\Services\Payment;      // Para servicios de pago
namespace App\Exceptions;            // Para excepciones personalizadas

Usar clases de otros namespaces

Para usar una clase de otro namespace, debes referirte a ella con su nombre completo (Fully Qualified Name o FQN):

PHP
<?php

namespace App\Controllers;

declare(strict_types=1);

class UsuarioController
{
    public function mostrar(int $id): void
    {
        // Nombre completo con \ inicial (FQN absoluto)
        $usuarioAuth = new \Auth\Usuario('admin', 'hash');

        // Nombre completo sin \ inicial (relativo al namespace actual)
        $usuarioBlog = new \Blog\Usuario(1, 'Admin', 'Bio del admin');

        echo $usuarioAuth->nombre;
        echo $usuarioBlog->nombrePublico;
    }
}
Barra invertida inicial

La \ inicial indica un nombre absoluto desde la raíz. Sin ella, PHP busca relativo al namespace actual. En la siguiente lección veremos use para evitar escribir nombres tan largos.

El namespace global

Las clases nativas de PHP (como DateTime, Exception) viven en el namespace global. Desde dentro de un namespace, debes usar \ para acceder a ellas:

PHP
<?php

namespace App\Servicios;

declare(strict_types=1);

class Logger
{
    public function log(string $mensaje): void
    {
        // DateTime es una clase global, necesita \
        $fecha = new \DateTime();

        // Exception también es global
        if ($mensaje === '') {
            throw new \InvalidArgumentException('El mensaje no puede estar vacío');
        }

        echo "[{$fecha->format('Y-m-d H:i:s')}] $mensaje\n";
    }
}

Funciones y constantes globales

Las funciones y constantes nativas de PHP se buscan primero en el namespace actual, y si no se encuentran, en el global:

PHP
<?php

namespace App\Utils;

declare(strict_types=1);

class StringHelper
{
    public function procesar(string $texto): string
    {
        // strlen() se busca en App\Utils primero, luego en global
        // Funciona sin \ porque no hay strlen() en este namespace
        $longitud = strlen($texto);

        // Para ser explícito, puedes usar \
        $mayusculas = \strtoupper($texto);

        // Constantes como PHP_EOL también funcionan
        return $mayusculas . PHP_EOL;
    }
}

Definir funciones y constantes en namespaces

No solo clases, también puedes definir funciones y constantes dentro de un namespace:

PHP
<?php

namespace App\Helpers;

declare(strict_types=1);

// Constante en el namespace
const VERSION = '1.0.0';

// Función en el namespace
function formatearPrecio(float $precio): string
{
    return number_format($precio, 2, ',', '.') . ' €';
}

// Uso desde otro archivo:
// echo \App\Helpers\VERSION;
// echo \App\Helpers\formatearPrecio(1234.56);

Ejemplo práctico: Estructura de una aplicación

PHP
<?php

// src/Models/Producto.php
namespace App\Models;

declare(strict_types=1);

class Producto
{
    public function __construct(
        public readonly int $id,
        public readonly string $nombre,
        public readonly float $precio
    ) {}
}
PHP
<?php

// src/Services/CarritoService.php
namespace App\Services;

declare(strict_types=1);

class CarritoService
{
    /** @var \App\Models\Producto[] */
    private array $productos = [];

    public function agregar(\App\Models\Producto $producto): void
    {
        $this->productos[] = $producto;
    }

    public function calcularTotal(): float
    {
        $total = 0.0;
        foreach ($this->productos as $producto) {
            $total += $producto->precio;
        }
        return $total;
    }

    public function vaciar(): void
    {
        $this->productos = [];
    }
}
PHP
<?php

// index.php
declare(strict_types=1);

require_once 'src/Models/Producto.php';
require_once 'src/Services/CarritoService.php';

// Usar nombres completos
$producto = new \App\Models\Producto(1, 'Laptop', 999.99);
$carrito = new \App\Services\CarritoService();

$carrito->agregar($producto);
echo "Total: " . $carrito->calcularTotal(); // Total: 999.99

Ejercicios

Ejercicio 1: Namespaces básicos

Crea dos clases Validador: una en el namespace App\Validators\Email con un método validar(string $email): bool que valide emails, y otra en App\Validators\Password con un método que valide que la contraseña tenga al menos 8 caracteres.

Ver solución
<?php

// src/Validators/Email/Validador.php
namespace App\Validators\Email;

declare(strict_types=1);

class Validador
{
    public function validar(string $email): bool
    {
        return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
    }
}

// src/Validators/Password/Validador.php
namespace App\Validators\Password;

declare(strict_types=1);

class Validador
{
    public function validar(string $password): bool
    {
        return strlen($password) >= 8;
    }
}

// Uso
$emailValidator = new \App\Validators\Email\Validador();
$passValidator = new \App\Validators\Password\Validador();

var_dump($emailValidator->validar('test@example.com')); // true
var_dump($passValidator->validar('12345678'));          // true

Ejercicio 2: Usando clases globales

En el namespace App\Logs, crea una clase FileLogger que tenga un método log(string $mensaje): void que use \DateTime para añadir la fecha al mensaje. Si el mensaje está vacío, debe lanzar una \InvalidArgumentException.

Ver solución
<?php

// src/Logs/FileLogger.php
namespace App\Logs;

declare(strict_types=1);

class FileLogger
{
    public function log(string $mensaje): void
    {
        if ($mensaje === '') {
            throw new \InvalidArgumentException(
                'El mensaje no puede estar vacío'
            );
        }

        $fecha = new \DateTime();
        $linea = sprintf(
            '[%s] %s',
            $fecha->format('Y-m-d H:i:s'),
            $mensaje
        );

        echo $linea . PHP_EOL;
    }
}

// Uso
$logger = new \App\Logs\FileLogger();
$logger->log('Usuario conectado');
// [2024-01-15 10:30:45] Usuario conectado

Ejercicio 3: Funciones en namespaces

Crea un archivo con el namespace App\Utils\Arrays que contenga una función aplanar(array $array): array que convierta un array multidimensional en uno simple. Demuestra cómo llamarla desde otro namespace usando el nombre completo.

Ver solución
<?php

// src/Utils/Arrays.php
namespace App\Utils\Arrays;

declare(strict_types=1);

function aplanar(array $array): array
{
    $resultado = [];

    foreach ($array as $elemento) {
        if (is_array($elemento)) {
            $resultado = array_merge($resultado, aplanar($elemento));
        } else {
            $resultado[] = $elemento;
        }
    }

    return $resultado;
}

// Desde otro namespace
namespace App\Controllers;

$datos = [[1, 2], [3, [4, 5]], 6];
$plano = \App\Utils\Arrays\aplanar($datos);

print_r($plano);
// [1, 2, 3, 4, 5, 6]

¿Te está gustando el curso?

Tenemos cursos premium con proyectos reales y soporte.

Descubrir cursos premium