Async PHP - atabegruslan/Notes GitHub Wiki

Handle async requests in PHP

Swoole

https://www.swoole.com/#http

$http = new Swoole\Http\Server('127.0.0.1', 9501);

$http->on('start', function ($server) {
    echo "Swoole http server is started at http://127.0.0.1:9501\n";
});

$http->on('request', function ($request, $response) {
    $response->header('Content-Type', 'text/plain');
    $response->end('Hello World');
});

$http->start();

Plain PHP

Laravel

  1. Make a API route. In routes/api.php
Route::get('/test', function() {
    return \App\Models\User::all();
});

API can be accessed by GET localhost/project/public/api/test

According to POSTMAN, it takes an average of 240ms

  1. Install Swoole

Run pecl install swoole

Use php -i | grep php.ini to find the correct php.ini

Add extension=swoole.so into php.ini

Run composer require swooletw/laravel-swoole

Add SwooleTW\Http\LaravelServiceProvider::class to the providers array of config/app.php

Run php artisan vendor:publish --tag=laravel-swoole

Run php artisan swoole:http start

Now API can be accessed by GET 127.0.0.1:1215/api/test

According to POSTMAN, it takes an average of 30ms

FrankenPHP

Laravel Octane

ReactPHP

https://reactphp.org

<?php

// $ composer require react/http react/socket # install example using Composer
// $ php example.php # run example on command line, requires no additional web server

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

$http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterface $request) {
    return React\Http\Message\Response::plaintext(
        "Hello World!\n"
    );
});

$socket = new React\Socket\SocketServer('127.0.0.1:8080');
$http->listen($socket);

echo "Server running at http://127.0.0.1:8080" . PHP_EOL;

Plain PHP

https://gist.github.com/greut/949850


Make asynchronous work

Process

Plain PHP

composer require symfony/process

index.php

<?php
require "vendor/autoload.php";
use Symfony\Component\Process\Process;

$process = new Process(['php', 'process.php']);
$process->start();

echo 2;

while ($process->isRunning())
{
    sleep(1);
}

echo $process->getOutput();

process.php

<?php
sleep(10);
echo 1;

Spatie

composer require spatie/async

<?php

require "vendor/autoload.php";

use Spatie\Async\Pool;

$pool = Pool::create();

$pool
    ->add(function () {
        sleep(10);
        return 1;
    })
    ->then(function ($output) {
        echo $output;
    })
    ->catch(function ($exception) {
        var_dump($exception);
    });

echo 2;

$pool->wait();

Threads

Queue

https://stackoverflow.com/questions/14236296/asynchronous-function-call-in-php

Plain PHP

Laravel


Make async requests

Async Guzzle

Theory: https://stackoverflow.com/questions/35655616/how-does-guzzle-send-async-web-requests

Regular cURL

<?php

// https://jsonplaceholder.typicode.com/
$url = 'https://jsonplaceholder.typicode.com/posts';

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
$response = curl_exec($ch);
curl_close($ch);

var_dump($response);

// ---

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);

$payload = json_encode(["title" => "foo", "body" => "bar", "userId" => 1]);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json', 'Content-Length: ' . strlen($payload)]);

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

var_dump($response);

Regular Guzzle

guzzlehttp/guzzle

<?php
// https://jsonplaceholder.typicode.com/
$base_url = 'https://jsonplaceholder.typicode.com';
$path = '/posts';

require "vendor/autoload.php";

use GuzzleHttp\Client;

// GET
$client = new Client([
    'base_uri' => $base_url,
]);
$response = $client->request('GET', $path, [
    // 'query' => [
    //     'param' => 'blah',
    // ]
]);
if ($response->getStatusCode() === 200)
{
	$body = $response->getBody();
	$arr_body = json_decode($body);
	var_dump($arr_body);
}

// POST
$client = new Client([
    'base_uri' => $base_url,
]);
$response = $client->request('POST', $path, [
    'json' => ["title" => "foo", "body" => "bar", "userId" => 1]
]);
if ($response->getStatusCode() === 200)
{
	$body = $response->getBody();
	$arr_body = json_decode($body);
	var_dump($arr_body);
}

Async Guzzle

<?php

// https://jsonplaceholder.typicode.com/
$base_url = 'https://jsonplaceholder.typicode.com';
$path = '/posts';

require "vendor/autoload.php";

use GuzzleHttp\Client;

// GET
$client = new Client();

// start request
$promise = $client->getAsync($base_url.$path)->then(
    function ($response) {
        echo 'test';
        sleep(10);echo 'test2';return;
        return $response->getBody();
    }, function ($exception) {
        return $exception->getMessage();
    }
);
 
// do other things
echo '<b>This will not wait for the previous get request to finish to be displayed!</b>';
 
// wait for request to finish and display its response
$response = $promise->wait();
echo $response;

// POST
$client = new Client();

// start request
$promise = $client->postAsync($base_url.$path, [ 'json' => ["title" => "foo", "body" => "bar", "userId" => 1] ])->then(
    function ($response) {
        return $response->getBody();
    }, function ($exception) {
        return $exception->getMessage();
    }
);
 
// do other things
echo '<b>This will not wait for the previous post request to finish to be displayed!</b>';
 
// wait for request to finish and display its response
$response = $promise->wait();
echo $response;
⚠️ **GitHub.com Fallback** ⚠️