PHP

Phast, el Composer en Rust: 3x más rápido pero no drop-in

Autorangel cruz
Publicado
Lectura11 min de lectura
Phast, el Composer en Rust: 3x más rápido pero no drop-in

Phast es un gestor de dependencias para PHP escrito en Rust que lee tu composer.json, descarga de Packagist y genera un vendor/autoload.php compatible con el de Composer. Lo medí contra Composer en un proyecto Laravel recién creado y es entre 2,5 y 7 veces más rápido según el escenario: el "3x más rápido" que circula en redes es real. Lo de "drop-in replacement" no lo es todavía: en la versión 0.1.0 los tests de Laravel fallan, los plugins de Composer no se ejecutan y tu composer.lock se ignora.

El tweet que lo puso a circular dice "same composer.json, same vendor/, way faster". Lo de "way faster" lo confirmo con números. Lo de "same composer.json" y "same vendor/" tiene letra chica, y conviene leerla antes de tocar el Dockerfile o el pipeline de CI.

Qué es Phast

Phast es un proyecto open source publicado en github.com/phast-sh/phast. Según su README, resuelve, descarga y genera el autoload de tus dependencias desde Packagist y cualquier registro compatible con Composer. Trae un formato propio, phast.toml para el manifiesto y phast.lock para el lock.

Los datos del proyecto:

  • La primera versión pública, la 0.1.0 "Ai-Petri", salió el 3 de noviembre de 2026. El README la presenta como beta y avisa que los formatos del manifiesto y del lock pueden cambiar antes de la 1.0.
  • El repositorio se creó en junio de 2026 y tiene más de 100 estrellas.
  • La licencia es doble, MIT o Apache 2.0, a elección.
  • Hay binarios para Linux y macOS. En otras plataformas se compila desde el código con cargo.
  • Casi todos los commits son de una sola persona, y el README aclara que todavía no acepta pull requests.

El modelo es el de uv con pip en Python: una herramienta nueva, escrita en Rust, sobre los registros que ya existen. Su guía de migración hace la comparación con uv de forma explícita.

"Same composer.json": qué lee Phast de tu proyecto

Phast lee tu composer.json, pero solo mientras no exista un phast.toml. En el código, la función que carga el manifiesto prefiere phast.toml y usa composer.json como respaldo: si corres phast install en un proyecto de Composer sin tocar nada, funciona.

La documentación empuja otro camino. La guía de migración propone correr phast migrate, que convierte el composer.json en un phast.toml una sola vez. A partir de ahí, según la guía, "Phast neither reads nor writes composer.json again". Si sigues el camino documentado, terminas con dos manifiestos y Composer deja de ver los cambios que hagas con phast add.

Con el lock la ruptura es total. Phast no lee composer.lock: en el código no aparece ni una vez fuera de los tests. La guía lo justifica (ese lock salió del resolver de Composer y no quieren heredar sus supuestos) y advierte la consecuencia: tus versiones pueden cambiar, porque Phast resuelve de nuevo y elige las más nuevas que permiten tus restricciones. El lock que commiteas pasa a ser phast.lock, un TOML con su propio formato.

La lección: después del primer phast install en un proyecto con lock, compara las versiones de phast.lock con las de tu composer.lock antes de hacer deploy.

"Same vendor/": el autoloader es de Phast

El vendor/ que genera Phast tiene los mismos paquetes, en las mismas versiones, que el de Composer. En mi prueba con un Laravel recién creado, los dos instalaron 110 paquetes y no hubo ni una versión distinta. Phast solo agrega una entrada __root__ en vendor/composer/installed.json.

El autoloader es otra cosa. Composer deja su ClassLoader en vendor/composer/; Phast genera el suyo en vendor/phast/, con la clase Phast\Autoload\ClassLoader, y la expone también bajo el nombre Composer\Autoload\ClassLoader para que el código que la busca la encuentre. La API \Composer\InstalledVersions funciona: pedí la versión de laravel/framework y devolvió v13.42.0, igual que con Composer. Lo que falla son los métodos del ClassLoader original que Phast no implementa, y Laravel usa uno de ellos en los tests.

Los números que publica Phast

Phast publica un benchmark en su documentación con dos proyectos: koel, una aplicación con 202 paquetes, y el skeleton de Laravel, con 109. Mediana de 5 corridas en un Apple M3 Pro:

Escenario koel Laravel skeleton
Instalación en frío (sin lock ni caché) 9,6x 2,4x
Desde un lock, caché vacía 3,3x 3,0x
Caché caliente 3,4x 5,7x
Nada que hacer (ya instalado) 5,9x 6,7x

El "3x" del tweet sale de la segunda fila, que es el caso de un runner de CI nuevo con el lock commiteado.

Compararon contra Composer 2.8.10, de julio de 2025, cuando la versión actual en Packagist es la 2.11.2. Y los resultados crudos que publicaron muestran que el 9,6x de koel en frío tiene mucho ruido. Composer tardó entre 61 y 195 segundos en esas cinco corridas, con una media de 98 segundos y una mediana de 67. Con tanta diferencia entre corridas, la mediana dice poco.

Mis números: Phast contra Composer 2.10 en un Laravel limpio

Para tener números propios, medí Phast 0.1.0 contra Composer 2.10.3, la versión que tengo instalada, sobre un laravel/laravel recién creado con composer create-project --no-install. El entorno: un Apple M1 con PHP 8.4.27 y mi conexión de casa. Hice 5 corridas por escenario, alternando las dos herramientas para repartir el ruido de red, y me quedé con la mediana:

Escenario Composer 2.10.3 Phast 0.1.0 Diferencia
En frío: sin lock, caché vacía 8,04 s 3,27 s 2,5x
Desde el lock, caché vacía 6,79 s 2,05 s 3,3x
Desde el lock, caché caliente 3,11 s 0,64 s 4,9x
Ya instalado, nada que hacer 1,43 s 0,20 s 7,2x

Las decisiones que pueden mover estos números:

  • Las dos herramientas corrieron sin scripts (--no-scripts), cada una con su propia carpeta de caché.
  • Composer corrió con el autoloader por defecto, sin -o. En el benchmark de Phast, Composer generó el autoloader optimizado, que tarda más: mis cifras le favorecen un poco a Composer.
  • Cada herramienta instaló desde su propio lock, generado en su primera corrida en frío.
  • Composer fue más irregular desde el lock con caché vacía: entre 5,65 y 8,30 segundos. Phast se quedó entre 2,01 y 2,22.

Coinciden con lo que publica Phast para el skeleton de Laravel, aunque medí con una Composer más nueva y en una máquina más vieja.

En un pipeline de CI con caché restaurada pasas de unos 3 segundos a menos de uno. Se nota, pero en un pipeline de varios minutos de tests no es la parte que más pesa.

Lo que se rompe hoy con Phast

Phast 0.1.0 tiene huecos concretos, y casi todos aparecen en proyectos comunes. Probé dos yo mismo y el resto está documentado en el repositorio.

Los tests de Laravel fallan

Instalé el skeleton de Laravel con Phast, con scripts. php artisan --version funciona y package:discover corre bien. Pero php artisan test falla en el test de ejemplo:

Failed opening required '/bootstrap/app.php'

La causa está en Application::inferBasePath() de Laravel, que llama a ClassLoader::getRegisteredLoaders() para averiguar dónde está el proyecto. El ClassLoader de Phast no tiene ese método. El mismo proyecto instalado con Composer pasa los dos tests. Hay un pull request abierto, el #24, que agrega el método. Mientras no se publique, ningún test de Laravel que arranque la aplicación funciona.

Los plugins de Composer no se ejecutan

Phast no ejecuta plugins de Composer. La referencia del manifiesto marca allow-plugins como reservado porque "plugin execution is not implemented yet". Suena a detalle hasta que piensas en composer/installers, el plugin que usa medio ecosistema de WordPress para mandar plugins y temas a wp-content/.

Lo probé con composer/installers y yoast/wordpress-seo desde Packagist, con un installer-paths hacia wp-content/plugins/. Composer deja Yoast en wp-content/plugins/wordpress-seo. Phast lo deja en vendor/yoast/wordpress-seo, donde WordPress nunca lo va a cargar. Y con WPackagist ni llega a instalar: falla porque el registro usa el formato antiguo de Composer, con providers-url, y Phast solo entiende metadata-url o paquetes inline.

Otros fallos reportados

Los issues abiertos en los días siguientes al lanzamiento muestran el resto:

  • #22: no puede resolver drupal/core porque no entiende la restricción self.version.
  • #27: el resolver falla de forma intermitente con guzzlehttp/guzzle y openai-php/client. El autor del issue reporta 5 fallos en 20 corridas con la misma configuración.
  • #21: se cae con scripts pre-autoload-dump que limpian vendor/, como el de aws/aws-sdk-php.

La guía de migración suma dos más: los scripts que llaman a composer directamente solo funcionan si Composer sigue instalado, y vendor-dir y bin-dir todavía no se soportan.

El patrón: la velocidad se nota, la compatibilidad se descubre

Medir la velocidad cuesta diez minutos. La compatibilidad se descubre cuando algo se rompe, y Phast tiene la mala suerte de romperse en sitios que no se ven en un install que termina en verde: el test que no arranca, el plugin que acaba en la carpeta equivocada, el lock que cambia versiones sin avisar.

Dicho eso, es una 0.1.0 publicada el 3 de noviembre, la documentación dice con claridad qué no funciona, y los issues se responden rápido: el de laravel-debugbar, que no se podía resolver, quedó arreglado el mismo día y sale en la próxima versión. Los números de velocidad son honestos y se pueden reproducir.

Pero Composer 2.10 tarda 3 segundos en un Laravel con caché caliente. Para ganar esos 2,5 segundos, hoy tendrías que cambiar el lock del equipo, revisar cada plugin y esperar que tus tests arranquen. Hoy no compensa.

Cuándo probar Phast y cuándo no

Pruébalo en un proyecto personal o en una rama, para ver qué tan lejos llega con tus dependencias. phast install funciona sobre un composer.json sin convertir nada, así que puedes medirlo sin tocar tu flujo: si no te gusta, borras phast.lock y vendor/ y vuelves a composer install.

No lo metas todavía en un proyecto Laravel con tests, en un sitio WordPress que dependa de composer/installers o en un pipeline de CI compartido. Para esos casos, lo que tiene que pasar primero es que se publique el arreglo del #24 y que haya soporte para plugins.

Si quieres entender por qué el lock importa tanto en esta discusión, escribí sobre la importancia del composer.lock en PHP. Y lo que trae la versión actual de Composer está en Composer 2.10: bloqueo de malware y políticas de dependencias.

Preguntas frecuentes

¿Qué es Phast?

Phast es un gestor de dependencias para PHP escrito en Rust y compatible con Composer. Descarga paquetes de Packagist, lee composer.json y genera un vendor/autoload.php que tu aplicación usa como el de Composer. Su primera versión pública, la 0.1.0, salió el 3 de noviembre de 2026.

¿Phast es más rápido que Composer?

Sí. En mi medición sobre un Laravel recién creado fue 2,5 veces más rápido en frío, 3,3 veces desde el lock con caché vacía, 4,9 veces con caché caliente y 7,2 veces cuando no había nada que instalar, frente a Composer 2.10.3.

¿Phast es un reemplazo directo de Composer?

Todavía no. Lee composer.json, pero no usa composer.lock, no ejecuta plugins de Composer como composer/installers y su autoloader no implementa getRegisteredLoaders(), lo que hace fallar los tests de Laravel en la versión 0.1.0.

¿Phast funciona con Laravel?

Instala Laravel y artisan funciona, incluido package:discover. Los tests fallan porque Laravel llama a ClassLoader::getRegisteredLoaders() para encontrar la raíz del proyecto, y el autoloader de Phast no tiene ese método. El pull request #24 lo agrega.

¿Phast funciona con WordPress y Composer?

No con los setups habituales. No ejecuta el plugin composer/installers, así que los plugins de WordPress terminan en vendor/ en lugar de wp-content/plugins/, y no puede leer el registro de WPackagist.

¿Cómo instalo Phast?

En Linux y macOS, con el script oficial: curl -fsSL https://raw.githubusercontent.com/phast-sh/phast/main/install.sh | sh. Instala el binario en ~/.local/bin y verifica su checksum. En otras plataformas se compila con cargo install.

Fuentes

¿Tienes un proyecto en mente?

Trabajo con Laravel, WordPress, SEO técnico y servidores MCP. El primer paso es una llamada de descubrimiento, sin costo ni compromiso, donde me cuentas qué necesitas y te digo con honestidad si puedo ayudarte.

Hablemos