miércoles, 25 de junio de 2025

Implementando Coroutines con Fibers en PHP 8.1+

Implementando Coroutines con Fibers en PHP 8.1+

PHP 8.1 introdujo las Fibers, un mecanismo de concurrencia ligero que permite implementar coroutines. Las coroutines son funciones que pueden suspender su ejecución y retomarla más tarde, sin necesidad de crear nuevos hilos o procesos. Esto permite escribir código asíncrono de manera más legible y eficiente que con callbacks o promesas.

Este post explora cómo implementar coroutines con Fibers para manejar operaciones de E/S concurrentes, como múltiples solicitudes HTTP, de forma eficiente y no bloqueante. Utilizaremos la extensión cURL para realizar las solicitudes HTTP.


<?php

use Fiber;

/**
 * Función para realizar una solicitud HTTP asíncrona usando cURL y Fibers.
 *
 * @param string $url La URL a solicitar.
 * @return string|false El contenido de la respuesta o false en caso de error.
 */
function async_http_get(string $url): string|false
{
    $fiber = Fiber::getCurrent(); // Obtener la Fiber actual
    if (!$fiber) {
        throw new LogicException("Esta función debe ser llamada dentro de una Fiber.");
    }

    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, 5); // Timeout de 5 segundos

    // Callback para ejecutar cuando cURL necesite leer/escribir
    curl_setopt($ch, CURLOPT_READFUNCTION, function ($ch, $fd, $length) use ($fiber) {
        // Ceder el control a la Fiber principal
        Fiber::suspend();
        return ''; // cURL necesita un valor de retorno, aunque sea vacío
    });

    $result = curl_exec($ch);
    curl_close($ch);

    return $result;
}

/**
 * Wrapper para ejecutar una coroutine dentro de una Fiber.
 *
 * @param callable $callable La coroutine a ejecutar.
 * @return mixed El resultado de la coroutine.
 */
function run(callable $callable): mixed
{
    $fiber = new Fiber($callable);
    return $fiber->start();
}
    

El código anterior define dos funciones clave: `async_http_get` y `run`. La función `async_http_get` utiliza la extensión cURL para realizar solicitudes HTTP de forma asíncrona. Dentro de la función, `Fiber::suspend()` cede el control a la Fiber principal, permitiendo que otras coroutines se ejecuten. La función `run` crea una nueva Fiber y la ejecuta.


<?php

require_once 'fibers.php'; // Incluir el código anterior

$urls = [
    'https://www.example.com',
    'https://www.php.net',
    'https://www.google.com',
];

$responses = [];
$fibers = [];

// Iniciar las Fibers para cada URL
foreach ($urls as $url) {
    $fibers[$url] = new Fiber(function () use ($url, &$responses) {
        $responses[$url] = async_http_get($url);
        echo "Completado: $url\n";
    });
    $fibers[$url]->start();
}


// Gestionar la ejecución de las Fibers
while (count($fibers) > 0) {
    foreach ($fibers as $url => $fiber) {
        if ($fiber->isSuspended()) {
            $fiber->resume(); // Reanudar la Fiber
        }

        if ($fiber->isTerminated()) {
            unset($fibers[$url]); // Eliminar la Fiber completada
        }
    }
}

// Imprimir las respuestas (opcional)
// var_dump($responses);
    

Este ejemplo muestra cómo iniciar múltiples Fibers para realizar solicitudes HTTP concurrentes. El bucle `while` gestiona la ejecución de las Fibers, reanudándolas cuando están suspendidas y eliminándolas cuando están terminadas. Esto permite que las solicitudes se realicen de forma concurrente sin bloquear el hilo principal.

En conclusión, las Fibers en PHP 8.1+ ofrecen una forma poderosa y eficiente de implementar coroutines para manejar operaciones de E/S concurrentes. Esta técnica puede mejorar significativamente el rendimiento de aplicaciones que realizan muchas operaciones de red.

No hay comentarios:

Publicar un comentario