Search code examples
phpasynchronousamphp

Async http call with php


I have a situation where I have a loop, that is going to read chunk of data from a file, send those chunk to a rest api, and continue until the EOF, but I want this to be async inside the loop, so, I don't have to wait until the API respond to read the next chunk. I have been looking at Amphp and ReactPHP for I can't find a solution to this, or maybe I don't understand how are those libraries supposed to be used. here is a pseudo of what I am doing.

<?php

while($file.read()){

   $chunk = getNextChunk();

   sendChunkAsync($chunk);

}

function getNextChunk(){

   echo "reading next chunk";

   // read next chunk of data

}

sample with amphp

function sendChunkAsync($chunk){

Loop::run(function () {

    $uri =  "https://testapi.com/api";

    $client = new DefaultClient;

    try {

            $promises = $client->request($uri);


        $responses = yield $promises;

       echo "chunk processed";

    } catch (Amp\Artax\HttpException $error) {

        // log error

        // $error->getMessage() . PHP_EOL;
    }
});

}

In this case I would expect (if reading chunk is faster than getting response from api) something like this, don't take this literary, I am trying to illustrate it for you.

Reading next chunk

Reading next chunk

chunk processed

Reading next chunk

chunk processed

chunk processed


Solution

  • I am going to use React as I know the library better but they work in similar ways.

    EDIT: updated, see comments

    This will read in a file and every time it recieves a chunk of data, it will create an api call and send the data off

    <?php
    
    require_once __DIR__ . '/vendor/autoload.php';
    
    function async_send($config, $file, callable $proccessor)
    {
    
        $config['ssl'] = true === $config['ssl'] ? 's' : '';
        $client = new \GuzzleHttp\Client([
            'base_uri' => 'http' . $config['ssl'] . '://' . $config['domain'] . '/rest/all/V1/',
            'verify' => false,
            'http_errors' => false
        ]);
        $loop = \React\EventLoop\Factory::create();
        $filesystem = \React\Filesystem\Filesystem::create($loop);
        $filesystem->getContents($file)->then(function($contents) use ($config, $proccessor, $client) {
            $contents = $proccessor($contents);
            $client->post($config['uri'], ['body' => $contents]);
        });
    }
    
    $config = [
        'domain' => 'example.com',
        'ssl' => true
    ];
    //somewhere later
    $configp['uri'] = 'products';
    async_send($configp, __DIR__ . 'my.csv', function ($contents) {
        return json_encode($contents);
    });