Saltar al contenido
composer.php · devschool

Composer, el gestor de dependencias de PHP

Lección 25 de 26 · 13 min de lectura · Actualizado el

En esta lección
  1. Qué es Composer y qué problema resuelve
  2. Instalar Composer
  3. Crear el composer.json
  4. Añadir una librería con composer require
  5. Usar la librería: vendor/autoload.php
  6. Un ejemplo muy útil: phpdotenv
  7. Autoload PSR-4 para tus propias clases
  8. composer.lock, vendor/ y Git
  9. Otros comandos útiles
  10. Errores frecuentes
  11. Resumen

Hasta ahora todo el código de tus proyectos lo has escrito tú. En el mundo real casi nadie programa así: para enviar emails, generar PDF, leer configuración o guardar registros de errores se usan librerías que otras personas ya han escrito y probado. Composer es la herramienta que las descarga, las mantiene al día y las carga en tu código. En esta lección aprenderás a instalarlo, a añadir librerías a un proyecto, a entender composer.json y composer.lock, y a usar su autoload para cargar también tus propias clases sin escribir un solo require.

Qué es Composer y qué problema resuelve

Imagina que en la web de una tienda de escalada quieres guardar en un archivo cada pedido y cada error. Podrías escribir tu propio sistema de registros, pero ya existe Monolog, una librería usada por millones de proyectos. Sin Composer tendrías que:

  1. Descargar el código de Monolog a mano.
  2. Descargar también las librerías de las que depende Monolog (y las dependencias de esas).
  3. Escribir un require por cada archivo que uses.
  4. Repetirlo todo cada vez que salga una versión nueva.

Composer hace todo eso por ti. Es un gestor de dependencias: una dependencia es cualquier librería que tu proyecto necesita para funcionar. Tú declaras qué librerías quieres y en qué versión, y Composer calcula qué hay que descargar, lo descarga en una carpeta vendor/ y genera un único archivo que carga todo.

Las librerías salen de Packagist (packagist.org), el repositorio público de paquetes de PHP. Cada paquete tiene un nombre con dos partes, proveedor/paquete: monolog/monolog, vlucas/phpdotenv, phpmailer/phpmailer…

Nota: si vienes de JavaScript, Composer es el equivalente a npm: composer.json hace el papel de package.json y vendor/ el de node_modules/. Lo viste en Node.js.

Instalar Composer

Composer es un programa escrito en PHP que se usa desde la terminal. Necesitas tener PHP instalado y accesible desde la línea de comandos (lo preparaste en Servidor local).

  • Windows: descarga el instalador Composer-Setup.exe desde getcomposer.org. Te pedirá la ruta de php.exe (con XAMPP suele ser C:\xampp\php\php.exe) y añadirá Composer al PATH.
  • macOS: con Homebrew, brew install composer.
  • Linux: muchas distribuciones lo tienen en su gestor de paquetes (sudo apt install composer en Ubuntu o Debian), aunque a veces es una versión antigua. La página de descarga de getcomposer.org explica cómo instalar la última.

Cierra y vuelve a abrir la terminal y comprueba que funciona:

composer --version

Debe mostrar algo como Composer version 2.8.x. Si dice “comando no encontrado”, el instalador no añadió Composer al PATH: reinicia el equipo o revisa la instalación.

Crear el composer.json

Cada proyecto que usa Composer tiene en su carpeta raíz un archivo composer.json: describe el proyecto y lista sus dependencias. Puedes crearlo con un asistente:

cd tienda-escalada
composer init

Te hará unas preguntas (nombre del paquete, descripción, autor, licencia…). Para un proyecto propio puedes aceptar casi todo con Intro y contestar “no” cuando te ofrezca buscar dependencias. El resultado mínimo es algo así:

{
    "name": "devschool/tienda-escalada",
    "description": "Tienda online de material de escalada",
    "type": "project",
    "require": {}
}

La sección importante es require: ahí aparecerán las librerías que instales.

Añadir una librería con composer require

Para añadir Monolog al proyecto:

composer require monolog/monolog

Composer hace cuatro cosas:

  1. Busca en Packagist la última versión compatible con tu PHP.
  2. La apunta en composer.json.
  3. Descarga Monolog y sus dependencias (en este caso psr/log) en vendor/.
  4. Crea composer.lock y genera vendor/autoload.php.

Tu composer.json queda así:

{
    "name": "devschool/tienda-escalada",
    "type": "project",
    "require": {
        "monolog/monolog": "^3.9"
    }
}

Y la carpeta del proyecto:

tienda-escalada/
├── composer.json
├── composer.lock
├── index.php
└── vendor/
    ├── autoload.php
    ├── composer/
    ├── monolog/
    └── psr/

Qué significa ^3.9

Las librerías siguen el versionado semántico: MAYOR.MENOR.PARCHE. Un cambio de PARCHE arregla errores, uno de MENOR añade cosas sin romper nada, y uno de MAYOR puede romper el código que ya funcionaba. Las restricciones de versión más usadas son:

RestricciónAceptaUso
^3.9>=3.9.0 y <4.0.0La habitual: actualizaciones seguras, nunca una versión mayor
~3.9>=3.9 y <4.0Parecida a ^ con dos cifras
~3.9.1>=3.9.1 y <3.10.0Solo parches
3.9.2Exactamente esaCuando algo falla con otras versiones
*CualquieraEvítala: una versión mayor nueva puede romper tu código

Si no indicas versión, composer require elige por ti una restricción con ^, que es lo recomendable.

Usar la librería: vendor/autoload.php

Composer genera un autoloader: un archivo que, cuando usas una clase que todavía no está cargada, busca su archivo y lo incluye automáticamente. Solo tienes que hacer un require de vendor/autoload.php al principio de tu script:

<?php
require __DIR__ . '/vendor/autoload.php';

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

$log = new Logger('tienda');
$log->pushHandler(new StreamHandler(__DIR__ . '/logs/app.log', Level::Info));

$log->info('Pedido recibido', ['pedido' => 1042, 'total' => 89.90]);
$log->warning('Stock bajo', ['producto' => 'Pies de gato talla 41', 'quedan' => 1]);
$log->debug('Esto no se guarda: está por debajo de Info');

Tras ejecutarlo, logs/app.log contiene (con tu fecha y hora):

[2026-10-06T10:15:41.667646+00:00] tienda.INFO: Pedido recibido {"pedido":1042,"total":89.9} []
[2026-10-06T10:15:41.668437+00:00] tienda.WARNING: Stock bajo {"producto":"Pies de gato talla 41","quedan":1} []

Fíjate en tres cosas:

  • use importa una clase de un espacio de nombres (namespace) para poder escribir Logger en vez de Monolog\Logger. Los espacios de nombres son como carpetas para clases: evitan que dos librerías con una clase Logger choquen entre sí.
  • El nivel (Level::Info) filtra lo que se guarda. Los niveles, de menos a más grave, son Debug, Info, Notice, Warning, Error, Critical, Alert y Emergency. En desarrollo puedes guardar desde Debug; en producción, desde Warning o Error.
  • El segundo argumento es el contexto: un array con datos útiles que se guardan como JSON junto al mensaje.

Consejo: en vez de usar echo o var_dump para saber qué pasa en un servidor real (donde nadie ve la pantalla), registra los errores con un logger. Cuando un cliente diga “ayer no pude pagar”, tendrás un archivo donde mirar.

Un ejemplo muy útil: phpdotenv

En la lección de PDO tendrás que conectar con MySQL, y eso exige un usuario y una contraseña. Nunca deben estar escritos en el código: si subes el proyecto a GitHub, cualquiera los verá. La práctica habitual es guardarlos en un archivo .env (de environment, entorno) que no se sube al repositorio, y leerlo con la librería vlucas/phpdotenv:

composer require vlucas/phpdotenv

El archivo .env, en la raíz del proyecto:

APP_ENTORNO=desarrollo
DB_HOST=localhost
DB_NAME=tienda
DB_USER=root
DB_PASS="clave con espacios"

Y el código que lo carga:

<?php
require __DIR__ . '/vendor/autoload.php';

$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();
$dotenv->required(['DB_HOST', 'DB_NAME', 'DB_USER'])->notEmpty();

echo $_ENV['DB_HOST'], "\n";
echo $_ENV['DB_PASS'], "\n";
echo $_ENV['APP_ENTORNO'] ?? 'produccion', "\n";
// localhost
// clave con espacios
// desarrollo
  • createImmutable(__DIR__) indica la carpeta donde está el .env. “Inmutable” significa que no sobrescribe variables de entorno que ya existan en el servidor, lo que permite que en producción el hosting defina las suyas.
  • load() lee el archivo y rellena $_ENV.
  • required(...)->notEmpty() lanza una excepción si falta alguna variable o está vacía. Así el fallo aparece al arrancar, con un mensaje claro, y no más tarde con un error de conexión confuso.

Junto a .env se suele subir un .env.example con las mismas claves pero sin valores reales. Quien descargue el proyecto lo copia como .env y pone sus datos.

Autoload PSR-4 para tus propias clases

El autoloader de Composer no solo sirve para las librerías: también puede cargar tus clases. Para eso sigue una norma llamada PSR-4, que relaciona el espacio de nombres de cada clase con la carpeta donde está su archivo. Las reglas son sencillas:

  • Cada clase va en su propio archivo, que se llama igual que la clase: Producto en Producto.php.
  • Eliges un espacio de nombres raíz para tu proyecto (por ejemplo Tienda) y lo asocias a una carpeta (normalmente src/).
  • Cada subespacio de nombres es una subcarpeta: Tienda\Modelo\Producto estará en src/Modelo/Producto.php.

Lo indicas en composer.json con la sección autoload:

{
    "name": "devschool/tienda-escalada",
    "type": "project",
    "require": {
        "monolog/monolog": "^3.9",
        "vlucas/phpdotenv": "^5.6"
    },
    "autoload": {
        "psr-4": {
            "Tienda\\": "src/"
        }
    }
}

La barra invertida va doblada porque en JSON \ se escapa, y el espacio de nombres termina siempre en \\. Después de cambiar la sección autoload, regenera el autoloader:

composer dump-autoload

Ahora crea las clases. src/Precio.php:

<?php
declare(strict_types=1);

namespace Tienda;

class Precio
{
    public static function conIva(float $base, float $iva = 21): float
    {
        return round($base * (1 + $iva / 100), 2);
    }
}

src/Modelo/Producto.php:

<?php
declare(strict_types=1);

namespace Tienda\Modelo;

class Producto
{
    public function __construct(
        public string $nombre,
        public float $precio,
        public int $stock = 0,
    ) {}
}

Y index.php, en la raíz, las usa sin incluir sus archivos:

<?php
declare(strict_types=1);

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

use Tienda\Precio;
use Tienda\Modelo\Producto;

$cuerda = new Producto('Cuerda 60 m', 120.0, 3);
echo $cuerda->nombre, ': ', Precio::conIva($cuerda->precio), " €\n";
// Cuerda 60 m: 145.2 €

Cuando PHP encuentra Tienda\Modelo\Producto, el autoloader quita el prefijo Tienda\, cambia las barras invertidas por barras de carpeta y añade .php: carga src/Modelo/Producto.php. Si añades más clases siguiendo la norma, no hace falta volver a ejecutar dump-autoload; solo cuando cambias la sección autoload del composer.json.

La estructura completa del proyecto queda así:

tienda-escalada/
├── composer.json
├── composer.lock
├── .env
├── .env.example
├── .gitignore
├── index.php
├── logs/
├── src/
│   ├── Precio.php
│   └── Modelo/
│       └── Producto.php
└── vendor/

Las clases, los constructores con promoción de propiedades y static se explican a fondo en Clases y objetos.

composer.lock, vendor/ y Git

Aquí está la parte que más se lía al principio. Hay dos archivos de dependencias y cada uno tiene su función:

  • composer.json dice lo que aceptas: “Monolog 3.9 o cualquier 3.x posterior”.
  • composer.lock dice lo que se instaló exactamente: “Monolog 3.9.0, psr/log 3.0.2”, con la dirección de descarga de cada uno.

Gracias al .lock, todas las personas del equipo y el servidor de producción tienen las mismas versiones exactas, aunque instalen meses después. Por eso:

Archivo o carpeta¿Se sube a Git?Motivo
composer.jsonSíLista de dependencias del proyecto
composer.lockSí (en proyectos y aplicaciones)Garantiza las mismas versiones en todas partes
vendor/NoSe regenera con composer install; puede pesar mucho
.envNoContiene contraseñas
.env.exampleSíSirve de plantilla

El archivo .gitignore del proyecto:

/vendor/
.env
/logs/*.log

Cuando alguien clona el repositorio, no tiene vendor/. Lo crea con:

composer install

install frente a update

Son los dos comandos que más se confunden:

  • composer install lee composer.lock e instala esas versiones exactas. Es el que usas al clonar un proyecto y al desplegarlo en el servidor. Si no hay .lock, se comporta como update.
  • composer update ignora el .lock, busca las versiones más nuevas que permite composer.json, las instala y reescribe el .lock. Úsalo cuando quieras actualizar a propósito, y prueba la web después.

Puedes actualizar una sola librería con composer update monolog/monolog.

Otros comandos útiles

ComandoQué hace
composer require paqueteAñade una dependencia
composer require --dev paqueteAñade una dependencia solo para desarrollo (en require-dev)
composer remove paqueteQuita una dependencia y borra sus archivos
composer showLista los paquetes instalados y sus versiones
composer outdatedMuestra cuáles tienen versiones más nuevas
composer auditBusca vulnerabilidades conocidas en tus dependencias
composer dump-autoloadRegenera el autoloader
composer install --no-dev --optimize-autoloaderInstalación para producción

Las dependencias de desarrollo son herramientas que solo necesitas mientras programas, como PHPUnit para las pruebas automáticas o PHPStan para analizar el código. Van en una sección aparte, require-dev, y en producción se omiten con --no-dev.

También puedes definir scripts, atajos con nombre para comandos que repites:

{
    "scripts": {
        "revisar": "php -l index.php"
    }
}

Y lanzarlo con composer revisar (en este caso, comprueba la sintaxis de index.php).

Cuidado: antes de instalar un paquete, mira en Packagist cuántas descargas tiene, cuándo se actualizó por última vez y si su repositorio está activo. Cada dependencia es código de otra persona que se ejecuta en tu servidor. Si un paquete lleva años abandonado, busca una alternativa.

Errores frecuentes

  • Olvidar require __DIR__ . '/vendor/autoload.php': aparece Class "Monolog\Logger" not found. Ese require va al principio de cada punto de entrada (index.php, por ejemplo).
  • Subir vendor/ a Git: llena el repositorio de miles de archivos ajenos. Añádelo a .gitignore y usa composer install.
  • No subir composer.lock: cada persona acaba con versiones distintas y aparecen errores “que en mi ordenador no pasan”.
  • Usar composer update en el servidor: puede instalar versiones que nunca has probado. En producción, siempre composer install.
  • Nombres que no siguen PSR-4: si la clase es Producto pero el archivo se llama producto.php, en Windows funcionará y en un servidor Linux no, porque distingue mayúsculas. El archivo debe llamarse exactamente igual que la clase.
  • Cambiar autoload y no ejecutar composer dump-autoload: el autoloader sigue con la configuración antigua y no encuentra tus clases.
  • Subir el .env: tus contraseñas quedan públicas. Si te ha pasado, cámbialas inmediatamente, aunque borres el archivo después.

Resumen

ConceptoQué recordar
ComposerGestor de dependencias de PHP; los paquetes salen de Packagist
composer.jsonLo que necesita el proyecto y con qué versiones (^3.9)
composer.lockVersiones exactas instaladas; se sube a Git
vendor/Código descargado; no se sube a Git
composer require x/yAñade una librería
composer installInstala lo que dice el .lock (al clonar o desplegar)
composer updateBusca versiones nuevas y reescribe el .lock
vendor/autoload.phpUn único require que carga todas las clases
PSR-4"Tienda\\": "src/": Tienda\Modelo\Producto está en src/Modelo/Producto.php
.env + phpdotenvConfiguración y contraseñas fuera del código

En la siguiente lección juntarás todo lo aprendido en el curso para construir una aplicación completa: Proyecto: libro de visitas.

Pon a prueba lo que has aprendido

[PHP] En composer.json aparece "monolog/monolog": "^3.9". ¿Qué versiones acepta?

[PHP] Clonas un proyecto que ya tiene composer.lock. ¿Qué comando debes usar para instalar las dependencias?

[PHP] Aparece "Class 'Monolog\Logger' not found" aunque has ejecutado composer require. ¿Qué falta casi seguro?

[PHP] ¿Qué debe subirse a Git y qué no?

¿Te ha quedado claro? Márcala y verás tu progreso en el explorador.