Desarrollo backend

PHP Fibers en 2026: programación asíncrona sin ReactPHP ni frameworks de terceros

Ruslan Ismailov Publicado 12 min de lectura
P

Introducción: la historia de la asincronía en PHP

PHP fue creado como un lenguaje síncrono para el procesamiento de solicitudes HTTP: una petición — un hilo — una respuesta. Durante mucho tiempo, la programación asíncrona en PHP estuvo asociada exclusivamente a soluciones de terceros: ReactPHP, Amp, Swoole o RoadRunner. Estas herramientas cumplían su cometido a la perfección, pero requerían aprender abstracciones ajenas, una configuración de entorno no trivial y abandonar las bibliotecas síncronas habituales.

En PHP 8.1 (noviembre de 2021) apareció el mecanismo de Fibers — un primitivo de multitarea cooperativa integrado directamente en el núcleo del lenguaje. Para 2024–2026, el ecosistema en torno a Fibers ha madurado notablemente: las herramientas de depuración han mejorado, han surgido abstracciones estables sobre la API básica y el framework Laravel ha comenzado a usar Fibers en sus mecanismos internos. En este artículo analizaremos cómo funcionan los Fibers, aprenderemos a escribir código concurrente sin dependencias externas y entenderemos dónde están los límites de aplicabilidad de este enfoque.

¿Qué es un Fiber: modelo de ejecución

Un Fiber (fibra) es un mecanismo ligero de cambio cooperativo del contexto de ejecución dentro de un único proceso PHP. A diferencia de los hilos del sistema (threads), los Fibers no se ejecutan en paralelo: en cada momento solo hay una fibra activa. El cambio se produce de forma explícita mediante la llamada a Fiber::suspend().

Veamos las diferencias clave con conceptos relacionados:

  • Generadores (Generators) — también permiten pausar la ejecución de una función mediante yield, pero no pueden transferir el control a código externo arbitrario fuera del generador. Un Fiber puede suspenderse desde cualquier punto de la pila de llamadas.
  • Corrutinas (Coroutines) — conceptualmente similares a los Fibers; en PHP, los Fibers son de hecho corrutinas simétricas completas con su propia pila de llamadas.
  • Threads (pthreads/parallel) — paralelismo real con pilas de memoria separadas y bloqueos. Los Fibers operan en un único hilo y no requieren sincronización de memoria compartida.
  • async/await (como en JS/Dart) — los Fibers son el bloque de construcción de bajo nivel sobre el que se puede implementar el azúcar sintáctico async/await.

El ciclo de vida de un Fiber atraviesa cuatro estados: createdrunningsuspendedterminated. El Fiber se inicia con start(), se suspende desde dentro mediante suspend(), se reanuda desde fuera con resume() y finaliza cuando la función callback termina su ejecución.

API de Fibers: análisis completo con ejemplos

Ejemplo básico

<?php

$fiber = new Fiber(function (): void {
    $value = Fiber::suspend('first suspension');
    echo "Resumed with: {$value}\n";

    Fiber::suspend('second suspension');
    echo "Fiber completed\n";
});

// Iniciamos el Fiber; la ejecución avanza hasta el primer suspend()
$result1 = $fiber->start();
echo "Suspended with: {$result1}\n"; // "first suspension"

// Reanudamos, pasando un valor al interior del Fiber
$result2 = $fiber->resume('hello');
echo "Suspended with: {$result2}\n"; // "second suspension"

// Finalizamos
$fiber->resume();
echo "Is terminated: " . ($fiber->isTerminated() ? 'yes' : 'no') . "\n";

El método Fiber::suspend($value) se llama desde dentro de la fibra y pasa un valor hacia afuera (que se convierte en el valor de retorno de start() o resume()). El valor pasado a resume($value) desde fuera se convierte en el valor de retorno de Fiber::suspend() dentro de la fibra.

Obtener el resultado con getReturn()

<?php

$fiber = new Fiber(function (): int {
    Fiber::suspend();
    return 42;
});

$fiber->start();
$fiber->resume();

if ($fiber->isTerminated()) {
    echo $fiber->getReturn(); // 42
}

El método getReturn() lanza una excepción FiberError si la fibra aún no ha terminado — algo importante a tener en cuenta al construir planificadores.

Patrones prácticos

Planificador de tareas simple con Fibers

El patrón central al trabajar con Fibers es el planificador cooperativo (cooperative scheduler). Mantiene una cola de fibras y las reanuda en orden hasta que todas finalizan.

<?php

class FiberScheduler
{
    /** @var Fiber[] */
    private array $queue = [];

    public function add(callable $callback): void
    {
        $this->queue[] = new Fiber($callback);
    }

    public function run(): void
    {
        // Iniciamos todas las fibras
        foreach ($this->queue as $fiber) {
            $fiber->start();
        }

        // Continuamos el bucle mientras haya fibras suspendidas
        while (true) {
            $active = array_filter(
                $this->queue,
                fn(Fiber $f) => $f->isSuspended()
            );

            if (empty($active)) {
                break;
            }

            foreach ($active as $fiber) {
                $fiber->resume();
            }
        }
    }
}

// Uso
$scheduler = new FiberScheduler();

$scheduler->add(function (): void {
    echo "Task 1: step A\n";
    Fiber::suspend();
    echo "Task 1: step B\n";
    Fiber::suspend();
    echo "Task 1: step C\n";
});

$scheduler->add(function (): void {
    echo "Task 2: step A\n";
    Fiber::suspend();
    echo "Task 2: step B\n";
});

$scheduler->run();
// Task 1: step A
// Task 2: step A
// Task 1: step B
// Task 2: step B
// Task 1: step C

Peticiones HTTP concurrentes con curl_multi y Fibers

El verdadero beneficio de los Fibers se manifiesta al integrarse con I/O no bloqueante. A continuación, un ejemplo de peticiones HTTP concurrentes mediante curl_multi sin bibliotecas externas:

<?php

function asyncGet(string $url): Fiber
{
    return new Fiber(function () use ($url): string {
        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT        => 10,
        ]);

        $mh = curl_multi_init();
        curl_multi_add_handle($mh, $ch);

        do {
            $status = curl_multi_exec($mh, $running);
            if ($running) {
                curl_multi_select($mh, 0.01); // select no bloqueante
                Fiber::suspend(); // cedemos el control al planificador
            }
        } while ($running && $status === CURLM_OK);

        $response = curl_multi_getcontent($ch);
        curl_multi_remove_handle($mh, $ch);
        curl_multi_close($mh);
        curl_close($ch);

        return $response;
    });
}

$urls = [
    'https://httpbin.org/delay/1',
    'https://httpbin.org/delay/2',
    'https://httpbin.org/uuid',
];

$fibers = array_map('asyncGet', $urls);

// Iniciamos todas las fibras
foreach ($fibers as $fiber) {
    $fiber->start();
}

// Ejecutamos el bucle de eventos
while (array_filter($fibers, fn($f) => !$f->isTerminated())) {
    foreach ($fibers as $fiber) {
        if ($fiber->isSuspended()) {
            $fiber->resume();
        }
    }
}

// Recopilamos los resultados
foreach ($fibers as $i => $fiber) {
    $body = $fiber->getReturn();
    echo "Response {$i}: " . substr($body, 0, 80) . "...\n";
}

Las tres peticiones se ejecutan concurrentemente dentro de un único proceso PHP sin ninguna extensión adicional, salvo el curl integrado.

Integración de Fibers con Laravel

Desde Laravel 9 (lanzado simultáneamente con PHP 8.1), el framework ha ido integrando progresivamente los Fibers en sus mecanismos internos. En 2026, los puntos de integración más relevantes son los siguientes:

  • Laravel Concurrency — el paquete laravel/concurrency, presentado en Laravel 11, proporciona la fachada Concurrency::run(), que internamente utiliza Fibers para la ejecución concurrente de closures dentro de un único worker.
  • Queue Workers — Horizon y el worker de colas integrado usan Fibers opcionalmente para procesar múltiples trabajos sin lanzar procesos adicionales.
  • HTTP ClientHttp::pool() en Laravel utiliza promesas de Guzzle, que con Fibers disponibles pueden migrarse al modelo cooperativo.
<?php

use Illuminate\Support\Facades\Concurrency;

// Laravel 11+: ejecución concurrente de tres tareas
[$users, $orders, $stats] = Concurrency::run([
    fn() => DB::table('users')->count(),
    fn() => DB::table('orders')->where('status', 'pending')->get(),
    fn() => Cache::remember('stats', 60, fn() => computeStats()),
]);

echo "Users: {$users}, Pending orders: " . $orders->count();

Si deseas ampliar el comportamiento y escribir tu propio planificador sobre Laravel, es conveniente registrarlo en el contenedor de servicios e inyectarlo mediante el constructor. Los Fibers funcionan perfectamente junto con el contenedor DI de Laravel, ya que no requieren modificar el estado global del framework.

Rendimiento: Fibers vs código síncrono vs Swoole

Es fundamental entender claramente que los Fibers no aceleran las tareas CPU-bound. La ganancia se obtiene exclusivamente en operaciones I/O-bound — peticiones de red, accesos a bases de datos, lectura de archivos — al solapar los tiempos de espera.

Resultados aproximados para 100 peticiones HTTP a una API externa (latencia ~200 ms cada una):

  • PHP síncrono — ~20 segundos (peticiones secuenciales).
  • PHP Fibers + curl_multi — ~0,8–1,2 segundos (concurrente, un proceso).
  • Swoole Coroutines — ~0,5–0,7 segundos (corrutinas nativas con event loop en C).
  • ReactPHP — ~0,6–0,9 segundos (promesas asíncronas sobre libuv/event).

Los Fibers quedan por detrás de Swoole en escenarios con miles de conexiones simultáneas, ya que el event loop de Swoole está escrito en C y optimizado a nivel de kernel. Sin embargo, para la mayoría de las aplicaciones de negocio la diferencia es insignificante, y la ventaja de los Fibers es que tienen cero dependencias y son totalmente compatibles con el entorno estándar PHP FPM.

Limitaciones y cuándo los Fibers no son la solución adecuada

Los Fibers son una herramienta poderosa, pero no una bala de plata. Estas son las situaciones en las que conviene elegir otro enfoque:

  • Cómputo intensivo de CPU — cifrado, renderizado de imágenes, análisis de archivos grandes. Aquí se necesita paralelismo real: la extensión parallel o delegar la tarea a un proceso o servicio separado.
  • Extensiones bloqueantes — si utilizas PDO síncrono, mysqli, file_get_contents sin envoltorios de flujo, el Fiber quedará bloqueado en I/O igual que el código normal. Los Fibers no convierten automáticamente el código bloqueante en no bloqueante.
  • Depuración compleja — la pila de llamadas al trabajar con Fibers puede ser no lineal, lo que dificulta el rastreo de errores en Xdebug. En 2026 la situación ha mejorado, pero sigue requiriendo atención.
  • Análisis estático — PHPStan y Psalm soportan Fibers, pero la tipificación de las firmas de suspend/resume sigue siendo poco intuitiva para desarrolladores junior.
  • Bibliotecas legadas — si una biblioteca de terceros usa register_shutdown_function o manejadores de errores globales, el comportamiento dentro de un Fiber puede ser inesperado.

Perspectivas: qué le espera al PHP asíncrono en 2026

PHP sigue evolucionando hacia una programación concurrente más cómoda. Algunas tendencias relevantes para 2026:

  • PHP 8.4 y posteriores — en el tracker de RFC se discuten la sintaxis nativa async/await sobre Fibers, un planificador mejorado en la biblioteca estándar y un event loop integrado a nivel de núcleo.
  • Amphp v3 — la biblioteca Amp ha sido completamente reescrita sobre Fibers y ofrece un rico conjunto de primitivos (Channel, DeferredFuture, EventLoop) sin necesidad de instalar Swoole.
  • Laravel Reverb y WebSockets — el servidor WebSocket oficial de Laravel, Reverb, usa Fibers internamente, demostrando que el ecosistema está listo para uso en producción.
  • FrankenPHP — un servidor PHP moderno en Go con soporte para el modo worker, donde los Fibers se convierten en la herramienta clave para procesar múltiples peticiones en un solo worker.
  • Estandarización de primitivos — en la comunidad PHP cobra fuerza el debate sobre incluir un planificador básico y primitivos de sincronización (Mutex, Channel) en la SPL.

«Los Fibers no son un sustituto de Swoole para alta carga. Son una forma de escribir código concurrente claro en PHP estándar, que funciona en cualquier hosting sin extensiones adicionales.» — consenso generalizado en la comunidad PHP hacia 2025–2026.

Conclusión

PHP Fibers, introducidos en la versión 8.1, para 2026 han evolucionado de una característica experimental a una herramienta madura apta para uso en producción. Permiten implementar multitarea cooperativa sin ReactPHP, Swoole ni otras dependencias pesadas — solo con PHP estándar y sus extensiones integradas.

Conclusiones clave del artículo:

  1. Los Fibers son corrutinas cooperativas con su propia pila de llamadas; no crean paralelismo, pero eliminan los tiempos de espera en I/O.
  2. La API es sencilla: new Fiber(callable), start(), Fiber::suspend(), resume(), getReturn().
  3. La combinación Fibers + curl_multi ofrece una mejora real de rendimiento de 10–20 veces en tareas de red comparado con el código secuencial.
  4. Laravel integra activamente los Fibers a través de la fachada Concurrency y componentes de servidor.
  5. Los Fibers no son adecuados para tareas CPU-bound y no convierten automáticamente el I/O bloqueante en no bloqueante.

Si eres desarrollador PHP y aún no has incorporado los Fibers a tu arsenal, es el momento ideal para empezar. El PHP estándar es capaz de mucho más de lo que se suele creer.

Tecnologías

Etiquetas

Ruslan Ismailov

Desarrollador Senior Web / Backend. Desarrollador senior web/backend con 9 años de experiencia. Stack: PHP, Laravel, PostgreSQL, Redis, Docker, Kubernetes, REST, microservicios, CI/CD. Más sobre mí →