Lección 56 de 75 12 min de lectura

Composer y PSR-4

Composer es el gestor de dependencias estándar de PHP. Además de instalar librerías, genera un autoloader optimizado que sigue el estándar PSR-4, el estándar de la industria para autoloading.

¿Qué es Composer?

Composer es una herramienta que gestiona las dependencias de tu proyecto PHP. Permite:

  • Instalar librerías de terceros (como PHPUnit, Monolog, etc.)
  • Gestionar versiones y actualizaciones
  • Generar un autoloader para tu código y las dependencias

Instalar Composer

Descarga e instala Composer desde getcomposer.org. Verifica la instalación:

Terminal
composer --version
# Composer version 2.x.x

Iniciar un proyecto con Composer

Crea un nuevo proyecto e inicializa Composer:

Terminal
mkdir mi-proyecto
cd mi-proyecto
composer init

Composer te hará preguntas sobre tu proyecto. Puedes aceptar los valores por defecto o personalizar. Esto crea composer.json:

JSON
{
    "name": "tu-usuario/mi-proyecto",
    "description": "Mi proyecto PHP",
    "type": "project",
    "require": {
        "php": ">=8.1"
    }
}

Configurar PSR-4 autoloading

PSR-4 es el estándar que define cómo mapear namespaces a directorios. Añade la configuración de autoload a composer.json:

JSON
{
    "name": "tu-usuario/mi-proyecto",
    "description": "Mi proyecto PHP",
    "type": "project",
    "require": {
        "php": ">=8.1"
    },
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    }
}

Esta configuración dice: "El namespace App\ corresponde al directorio src/".

Generar el autoloader

Después de configurar el autoload, genera los archivos necesarios:

Terminal
composer dump-autoload

Esto crea la carpeta vendor/ con el autoloader. Ahora la estructura es:

Estructura
mi-proyecto/
├── composer.json
├── composer.lock
├── vendor/
│   └── autoload.php    # Este es el autoloader
└── src/                # Aquí va tu código (App\)

Usar el autoloader de Composer

Solo necesitas un require al autoloader de Composer:

PHP
<?php

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

require_once __DIR__ . '/vendor/autoload.php';

use App\Models\Usuario;
use App\Services\AuthService;

// Las clases se cargan automáticamente
$usuario = new Usuario(1, 'ana@example.com');
$auth = new AuthService();

Ejemplo práctico con PSR-4

PHP
<?php

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

declare(strict_types=1);

class Usuario
{
    public function __construct(
        public readonly int $id,
        public readonly string $email,
        public readonly string $nombre = ''
    ) {}

    public function saludo(): string
    {
        $nombre = $this->nombre ?: 'Usuario';
        return "Hola, $nombre";
    }
}
PHP
<?php

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

declare(strict_types=1);

use App\Models\Usuario;
use InvalidArgumentException;

class UsuarioService
{
    /** @var Usuario[] */
    private array $usuarios = [];

    public function registrar(string $email, string $nombre): Usuario
    {
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            throw new InvalidArgumentException('Email inválido');
        }

        $id = count($this->usuarios) + 1;
        $usuario = new Usuario($id, $email, $nombre);
        $this->usuarios[$id] = $usuario;

        return $usuario;
    }

    public function buscar(int $id): ?Usuario
    {
        return $this->usuarios[$id] ?? null;
    }
}
PHP
<?php

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

require_once __DIR__ . '/vendor/autoload.php';

use App\Services\UsuarioService;

$servicio = new UsuarioService();

$ana = $servicio->registrar('ana@example.com', 'Ana García');
$luis = $servicio->registrar('luis@example.com', 'Luis López');

echo $ana->saludo() . "\n";  // Hola, Ana García
echo $luis->saludo() . "\n"; // Hola, Luis López

Autoload para tests

Para tests, usa autoload-dev que solo se carga en desarrollo:

JSON
{
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\": "tests/"
        }
    }
}

Ejecuta composer dump-autoload después de cambiar la configuración.

Instalar dependencias

Composer también gestiona librerías de terceros. Para instalar una dependencia:

Terminal
# Instalar una librería
composer require monolog/monolog

# Instalar como dependencia de desarrollo
composer require --dev phpunit/phpunit

Las dependencias se descargan a vendor/ y el autoloader las incluye automáticamente:

PHP
<?php

declare(strict_types=1);

require_once __DIR__ . '/vendor/autoload.php';

use Monolog\Logger;
use Monolog\Handler\StreamHandler;

// Monolog se carga automáticamente
$log = new Logger('app');
$log->pushHandler(new StreamHandler('app.log', Logger::INFO));

$log->info('Aplicación iniciada');

Optimizar para producción

En producción, optimiza el autoloader para mejor rendimiento:

Terminal
# Genera un classmap optimizado
composer dump-autoload --optimize

# O al instalar dependencias
composer install --optimize-autoloader --no-dev
.gitignore

Añade vendor/ a tu .gitignore. Las dependencias se reinstalan con composer install usando composer.lock.

Comandos esenciales de Composer

Terminal
# Inicializar proyecto
composer init

# Instalar dependencias (desde composer.lock)
composer install

# Añadir dependencia
composer require vendor/paquete

# Añadir dependencia de desarrollo
composer require --dev vendor/paquete

# Actualizar dependencias
composer update

# Regenerar autoloader
composer dump-autoload

# Ver dependencias instaladas
composer show

Ejercicios

Ejercicio 1: Proyecto con Composer

Crea un nuevo proyecto con Composer. Configura PSR-4 para que App\ apunte a src/. Crea las clases App\Models\Tarea y App\Services\TareaService. Verifica que el autoloading funciona creando un index.php que use ambas clases.

Ver solución
// composer.json
{
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    }
}
<?php

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

declare(strict_types=1);

class Tarea
{
    public function __construct(
        public readonly string $titulo,
        public bool $completada = false
    ) {}
}

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

declare(strict_types=1);

use App\Models\Tarea;

class TareaService
{
    private array $tareas = [];

    public function agregar(Tarea $tarea): void
    {
        $this->tareas[] = $tarea;
    }

    public function listar(): array
    {
        return $this->tareas;
    }
}

// index.php
require 'vendor/autoload.php';

use App\Models\Tarea;
use App\Services\TareaService;

$service = new TareaService();
$service->agregar(new Tarea('Aprender PHP'));
print_r($service->listar());

Ejercicio 2: Múltiples namespaces

Extiende el proyecto anterior para tener dos prefijos de namespace: App\ en src/ y Tests\ en tests/. Crea una clase Tests\Services\TareaServiceTest que instancie TareaService.

Ver solución
// composer.json
{
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\": "tests/"
        }
    }
}
<?php

// tests/Services/TareaServiceTest.php
namespace Tests\Services;

declare(strict_types=1);

use App\Models\Tarea;
use App\Services\TareaService;

class TareaServiceTest
{
    public function testAgregarTarea(): bool
    {
        $service = new TareaService();
        $tarea = new Tarea('Test');

        $service->agregar($tarea);
        $tareas = $service->listar();

        return count($tareas) === 1
            && $tareas[0]->titulo === 'Test';
    }
}

// Ejecutar: composer dump-autoload

Ejercicio 3: Instalar una dependencia

Instala la librería ramsey/uuid con Composer. Modifica la clase Tarea para que genere un UUID automáticamente al crearse. Consulta la documentación de la librería para ver cómo usarla.

Ver solución
# Terminal
composer require ramsey/uuid
<?php

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

declare(strict_types=1);

use Ramsey\Uuid\Uuid;

class Tarea
{
    public readonly string $id;

    public function __construct(
        public readonly string $titulo,
        public bool $completada = false
    ) {
        $this->id = Uuid::uuid4()->toString();
    }
}

// Uso
$tarea = new Tarea('Aprender Composer');
echo $tarea->id;
// Ejemplo: 550e8400-e29b-41d4-a716-446655440000

¿Te está gustando el curso?

Tenemos cursos premium con proyectos reales y soporte.

Descubrir cursos premium